@birdycom/omniaui 0.0.0-stage → 0.11.1

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 (216) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +21 -3
  3. package/bin/lib/add.mjs +126 -0
  4. package/bin/lib/catalog.mjs +169 -0
  5. package/bin/lib/project.mjs +90 -0
  6. package/bin/omniaui.mjs +70 -0
  7. package/dist/code-block.css +163 -0
  8. package/dist/components-extra.css +916 -0
  9. package/dist/components.css +10171 -0
  10. package/dist/components.min.css +1 -0
  11. package/dist/data-table.d.ts +9 -0
  12. package/dist/data-table.js +505 -0
  13. package/dist/docs-ui.css +267 -0
  14. package/dist/oc-theme.css +69 -0
  15. package/dist/parts/.gitkeep +0 -0
  16. package/dist/parts/app-accordion-accent.css +60 -0
  17. package/dist/parts/app-alpha-tabs.css +68 -0
  18. package/dist/parts/app-busy-overlay.css +25 -0
  19. package/dist/parts/app-column-menu.css +85 -0
  20. package/dist/parts/app-confirm-dialog.css +70 -0
  21. package/dist/parts/app-cookie-consent.css +63 -0
  22. package/dist/parts/app-data-table.css +174 -0
  23. package/dist/parts/app-greeting.css +47 -0
  24. package/dist/parts/app-list-toolbar.css +81 -0
  25. package/dist/parts/app-page-header.css +54 -0
  26. package/dist/parts/app-severity-card.css +91 -0
  27. package/dist/parts/app-sidebar-dark.css +15 -0
  28. package/dist/parts/app-sortable-row.css +52 -0
  29. package/dist/parts/app-stat-card.css +68 -0
  30. package/dist/parts/app-table-actions.css +71 -0
  31. package/dist/parts/app-table-compact.css +54 -0
  32. package/dist/parts/app-top-progress.css +68 -0
  33. package/dist/parts/app-tree.css +104 -0
  34. package/dist/parts/app-upgrade-banner.css +90 -0
  35. package/dist/parts/app-version-banner.css +80 -0
  36. package/dist/parts/data-chart-line.css +167 -0
  37. package/dist/parts/forms-affix-input.css +75 -0
  38. package/dist/parts/forms-barcode-input.css +75 -0
  39. package/dist/parts/forms-chip-filter.css +91 -0
  40. package/dist/parts/forms-color-select.css +97 -0
  41. package/dist/parts/forms-decimal-input.css +51 -0
  42. package/dist/parts/forms-filter-counts.css +94 -0
  43. package/dist/parts/forms-language-select.css +89 -0
  44. package/dist/parts/forms-number-input.css +98 -0
  45. package/dist/parts/forms-password-requirements.css +55 -0
  46. package/dist/parts/forms-phone-input.css +85 -0
  47. package/dist/parts/forms-time-input.css +74 -0
  48. package/dist/parts/forms-toggle-field.css +69 -0
  49. package/dist/parts/media-art.css +21 -0
  50. package/dist/parts/media-attachment.css +160 -0
  51. package/dist/parts/media-browser-frame.css +73 -0
  52. package/dist/parts/media-card.css +83 -0
  53. package/dist/parts/media-chat-composer.css +90 -0
  54. package/dist/parts/media-doc-viewer.css +151 -0
  55. package/dist/parts/media-emoji-picker.css +88 -0
  56. package/dist/parts/media-entity-card.css +52 -0
  57. package/dist/parts/media-flip-card.css +78 -0
  58. package/dist/parts/media-image-zoom.css +68 -0
  59. package/dist/parts/media-lightbox.css +131 -0
  60. package/dist/parts/media-message-thread.css +136 -0
  61. package/dist/parts/media-message.css +160 -0
  62. package/dist/parts/media-row.css +78 -0
  63. package/dist/parts/media-stack-card.css +82 -0
  64. package/dist/parts/media-terminal-log.css +76 -0
  65. package/dist/parts/media-thumb.css +71 -0
  66. package/dist/parts/site-fact-list.css +25 -0
  67. package/dist/parts/site-link-arrow.css +34 -0
  68. package/dist/parts/site-marquee.css +34 -0
  69. package/dist/parts/site-next-teaser.css +75 -0
  70. package/dist/parts/site-price-card.css +91 -0
  71. package/dist/parts/site-product-mark.css +66 -0
  72. package/dist/parts/site-project-card.css +111 -0
  73. package/dist/parts/site-prose.css +124 -0
  74. package/dist/parts/site-stack-list.css +48 -0
  75. package/dist/parts/site-timeline.css +54 -0
  76. package/dist/showcase.css +404 -0
  77. package/manifest.json +1108 -0
  78. package/package.json +69 -6
  79. package/snippets/accordion-accent.html +24 -0
  80. package/snippets/accordion.html +11 -0
  81. package/snippets/affix-input.html +30 -0
  82. package/snippets/alert-dialog.html +22 -0
  83. package/snippets/alert.html +5 -0
  84. package/snippets/alpha-tabs.html +37 -0
  85. package/snippets/aspect-ratio.html +15 -0
  86. package/snippets/attachment.html +43 -0
  87. package/snippets/avatar.html +6 -0
  88. package/snippets/back-link.html +5 -0
  89. package/snippets/badge.html +5 -0
  90. package/snippets/barcode-input.html +18 -0
  91. package/snippets/breadcrumb.html +6 -0
  92. package/snippets/browser-frame.html +18 -0
  93. package/snippets/busy-overlay.html +31 -0
  94. package/snippets/button-group.html +21 -0
  95. package/snippets/button.html +8 -0
  96. package/snippets/calendar.html +43 -0
  97. package/snippets/card.html +17 -0
  98. package/snippets/carousel.html +21 -0
  99. package/snippets/chart-line.html +46 -0
  100. package/snippets/chart.html +27 -0
  101. package/snippets/chat-composer.html +21 -0
  102. package/snippets/checkbox.html +5 -0
  103. package/snippets/checklist.html +23 -0
  104. package/snippets/chip-filter.html +14 -0
  105. package/snippets/code-window.html +20 -0
  106. package/snippets/collapsible.html +19 -0
  107. package/snippets/color-field.html +13 -0
  108. package/snippets/color-select.html +18 -0
  109. package/snippets/column-menu.html +38 -0
  110. package/snippets/combobox.html +19 -0
  111. package/snippets/command.html +24 -0
  112. package/snippets/confirm-dialog.html +32 -0
  113. package/snippets/context-menu.html +25 -0
  114. package/snippets/cookie-consent.html +70 -0
  115. package/snippets/copy-field.html +19 -0
  116. package/snippets/counter.html +2 -0
  117. package/snippets/data-table.html +151 -0
  118. package/snippets/date-picker.html +32 -0
  119. package/snippets/decimal-input.html +20 -0
  120. package/snippets/definition-list.html +20 -0
  121. package/snippets/demo-panel.html +18 -0
  122. package/snippets/doc-viewer.html +58 -0
  123. package/snippets/drawer.html +30 -0
  124. package/snippets/dropdown-menu.html +27 -0
  125. package/snippets/dropdown.html +9 -0
  126. package/snippets/dropzone.html +13 -0
  127. package/snippets/emoji-picker.html +51 -0
  128. package/snippets/empty-state.html +17 -0
  129. package/snippets/entity-card.html +26 -0
  130. package/snippets/expandable-table.html +26 -0
  131. package/snippets/fact-list.html +14 -0
  132. package/snippets/filter-counts.html +30 -0
  133. package/snippets/flip-card.html +22 -0
  134. package/snippets/form-field.html +6 -0
  135. package/snippets/greeting.html +22 -0
  136. package/snippets/heading.html +14 -0
  137. package/snippets/hover-card.html +18 -0
  138. package/snippets/image-zoom.html +30 -0
  139. package/snippets/input-group.html +10 -0
  140. package/snippets/input.html +5 -0
  141. package/snippets/item.html +26 -0
  142. package/snippets/kbd.html +13 -0
  143. package/snippets/label.html +9 -0
  144. package/snippets/language-select.html +26 -0
  145. package/snippets/launcher-card.html +14 -0
  146. package/snippets/lightbox.html +50 -0
  147. package/snippets/link-arrow.html +10 -0
  148. package/snippets/list-toolbar.html +33 -0
  149. package/snippets/marquee.html +15 -0
  150. package/snippets/media-card.html +28 -0
  151. package/snippets/media-row.html +21 -0
  152. package/snippets/media-thumb.html +12 -0
  153. package/snippets/menubar.html +30 -0
  154. package/snippets/message-thread.html +123 -0
  155. package/snippets/message.html +51 -0
  156. package/snippets/modal.html +15 -0
  157. package/snippets/native-select.html +15 -0
  158. package/snippets/navmenu.html +23 -0
  159. package/snippets/next-teaser.html +15 -0
  160. package/snippets/number-input.html +27 -0
  161. package/snippets/otp-input.html +9 -0
  162. package/snippets/page-header.html +24 -0
  163. package/snippets/pagination.html +6 -0
  164. package/snippets/password-input.html +24 -0
  165. package/snippets/password-requirements.html +30 -0
  166. package/snippets/phone-input.html +22 -0
  167. package/snippets/popover.html +15 -0
  168. package/snippets/price-card.html +34 -0
  169. package/snippets/product-mark.html +13 -0
  170. package/snippets/progress.html +4 -0
  171. package/snippets/project-card.html +23 -0
  172. package/snippets/prose.html +54 -0
  173. package/snippets/radio.html +17 -0
  174. package/snippets/recovery-codes.html +20 -0
  175. package/snippets/resizable.html +16 -0
  176. package/snippets/scroll-area.html +20 -0
  177. package/snippets/search.html +8 -0
  178. package/snippets/segmented.html +11 -0
  179. package/snippets/select.html +7 -0
  180. package/snippets/separator.html +15 -0
  181. package/snippets/severity-card.html +35 -0
  182. package/snippets/sheet.html +32 -0
  183. package/snippets/sidebar-docs.html +24 -0
  184. package/snippets/sidebar.html +55 -0
  185. package/snippets/site-footer.html +6 -0
  186. package/snippets/site-header.html +26 -0
  187. package/snippets/skeleton.html +4 -0
  188. package/snippets/slider.html +17 -0
  189. package/snippets/sortable-row.html +31 -0
  190. package/snippets/spinner.html +14 -0
  191. package/snippets/stack-card.html +17 -0
  192. package/snippets/stack-list.html +16 -0
  193. package/snippets/stat-card.html +24 -0
  194. package/snippets/stepper.html +13 -0
  195. package/snippets/switch.html +6 -0
  196. package/snippets/table-actions.html +39 -0
  197. package/snippets/table-compact.html +24 -0
  198. package/snippets/table.html +29 -0
  199. package/snippets/tabs.html +9 -0
  200. package/snippets/tag-input.html +60 -0
  201. package/snippets/terminal-log.html +23 -0
  202. package/snippets/textarea.html +3 -0
  203. package/snippets/theme-switch.html +6 -0
  204. package/snippets/time-input.html +26 -0
  205. package/snippets/timeline.html +28 -0
  206. package/snippets/toast-stack.html +10 -0
  207. package/snippets/toast.html +36 -0
  208. package/snippets/toc.html +13 -0
  209. package/snippets/toggle-field.html +20 -0
  210. package/snippets/toggle.html +22 -0
  211. package/snippets/tooltip.html +4 -0
  212. package/snippets/top-progress.html +12 -0
  213. package/snippets/tree.html +38 -0
  214. package/snippets/upgrade-banner.html +28 -0
  215. package/snippets/user.html +15 -0
  216. package/snippets/version-banner.html +15 -0
@@ -0,0 +1,916 @@
1
+ /* File: packages/ui/src/components-extra.css */
2
+ /* Map: spinner · kbd · slider · toggle + toggle-group · item · aspect-ratio · hover-card · menu extras (sub-menu, check/radio items, shortcut) ·
3
+ menubar · context-menu · carousel · resizable · combobox · drawer · native-select (base-select) · toast stack · calendar · date-picker · chart tokens */
4
+ /* Depends on: components.css (tokens --oc-*, .oc-btn, .oc-dropdown-*, .oc-popover, .oc-toast). Loaded after it in the concatenated bundle.
5
+ Every color is an --oc-* theme token or an Omnia palette token, so light and dark follow the theme.
6
+ Native HTML first (popover, dialog, :has(), anchor positioning, scroll-snap); progressive enhancement is wrapped in @supports. */
7
+
8
+ /* ===================================================================
9
+ SPINNER — border ring, inherits currentColor. Sizes: --sm · --lg · --xl. Color: set `color` or use --primary.
10
+ Add role="status" + aria-label (or visually hidden text) so it is announced.
11
+ =================================================================== */
12
+ .oc-spinner {
13
+ --oc-spinner-size: 1.25rem;
14
+ display: inline-block;
15
+ flex: none;
16
+ inline-size: var(--oc-spinner-size);
17
+ block-size: var(--oc-spinner-size);
18
+ vertical-align: -0.2em;
19
+ border: max(2px, calc(var(--oc-spinner-size) / 10)) solid color-mix(in oklab, currentColor 22%, transparent);
20
+ border-block-start-color: currentColor;
21
+ border-radius: 50%;
22
+ animation: oc-spinner-rotate 0.75s linear infinite;
23
+ }
24
+ .oc-spinner--sm { --oc-spinner-size: 0.875rem; }
25
+ .oc-spinner--lg { --oc-spinner-size: 2rem; }
26
+ .oc-spinner--xl { --oc-spinner-size: 3rem; }
27
+ .oc-spinner--primary { color: var(--oc-primary); }
28
+ .oc-spinner--muted { color: var(--oc-muted-foreground); }
29
+ @keyframes oc-spinner-rotate { to { transform: rotate(360deg); } }
30
+ @media (prefers-reduced-motion: reduce) {
31
+ .oc-spinner { animation-duration: 2.5s; }
32
+ }
33
+
34
+ /* ===================================================================
35
+ KBD — keyboard key. Group several keys with .oc-kbd-group. Inside a button/tooltip/menu it adapts to the surface.
36
+ =================================================================== */
37
+ .oc-kbd {
38
+ display: inline-flex;
39
+ align-items: center;
40
+ justify-content: center;
41
+ min-inline-size: 1.4rem;
42
+ block-size: 1.4rem;
43
+ padding: 0 0.35rem;
44
+ font: 600 0.6875rem/1 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
45
+ color: var(--oc-muted-foreground);
46
+ background: var(--oc-muted);
47
+ border: 1px solid var(--oc-border);
48
+ border-block-end-width: 2px;
49
+ border-radius: calc(var(--oc-radius) - 3px);
50
+ white-space: nowrap;
51
+ user-select: none;
52
+ }
53
+ .oc-kbd-group { display: inline-flex; align-items: center; gap: 0.25rem; }
54
+ .oc-kbd-group > .oc-kbd-sep { color: var(--oc-muted-foreground); font-size: 0.75rem; }
55
+
56
+ /* ===================================================================
57
+ SLIDER — native <input type="range" class="oc-slider">. Value, step, min/max, keyboard and a11y come from the browser.
58
+ Fill: Firefox fills natively; for Chromium/WebKit set --oc-slider-progress (e.g. style="--oc-slider-progress: 40%"),
59
+ optionally kept in sync by the 1-line script shown in the docs. Vertical: .oc-slider--vertical.
60
+ =================================================================== */
61
+ .oc-slider {
62
+ --oc-slider-progress: 0%;
63
+ --oc-slider-track: 0.375rem;
64
+ --oc-slider-thumb: 1.125rem;
65
+ appearance: none;
66
+ display: block;
67
+ inline-size: 100%;
68
+ block-size: var(--oc-slider-thumb);
69
+ margin: 0;
70
+ padding: 0;
71
+ background: transparent;
72
+ cursor: pointer;
73
+ accent-color: var(--oc-primary);
74
+ }
75
+ .oc-slider:focus { outline: none; }
76
+ .oc-slider::-webkit-slider-runnable-track {
77
+ block-size: var(--oc-slider-track);
78
+ border-radius: 999px;
79
+ background: linear-gradient(to right, var(--oc-primary) var(--oc-slider-progress), var(--oc-muted) var(--oc-slider-progress));
80
+ box-shadow: inset 0 0 0 1px var(--oc-border);
81
+ }
82
+ .oc-slider::-webkit-slider-thumb {
83
+ appearance: none;
84
+ inline-size: var(--oc-slider-thumb);
85
+ block-size: var(--oc-slider-thumb);
86
+ margin-block-start: calc((var(--oc-slider-track) - var(--oc-slider-thumb)) / 2);
87
+ border-radius: 50%;
88
+ border: 2px solid var(--oc-primary);
89
+ background: var(--oc-background);
90
+ box-shadow: 0 1px 3px oklch(from var(--color-black-60) l c h / 0.25);
91
+ transition: box-shadow var(--oc-transition-fast), scale var(--oc-transition-fast);
92
+ }
93
+ .oc-slider::-moz-range-track {
94
+ block-size: var(--oc-slider-track);
95
+ border-radius: 999px;
96
+ background: var(--oc-muted);
97
+ box-shadow: inset 0 0 0 1px var(--oc-border);
98
+ }
99
+ .oc-slider::-moz-range-progress { block-size: var(--oc-slider-track); border-radius: 999px; background: var(--oc-primary); }
100
+ .oc-slider::-moz-range-thumb {
101
+ box-sizing: border-box;
102
+ inline-size: var(--oc-slider-thumb);
103
+ block-size: var(--oc-slider-thumb);
104
+ border-radius: 50%;
105
+ border: 2px solid var(--oc-primary);
106
+ background: var(--oc-background);
107
+ box-shadow: 0 1px 3px oklch(from var(--color-black-60) l c h / 0.25);
108
+ }
109
+ .oc-slider:hover::-webkit-slider-thumb { scale: 1.08; }
110
+ .oc-slider:focus-visible::-webkit-slider-thumb { box-shadow: 0 0 0 4px oklch(from var(--oc-ring) l c h / 0.28); }
111
+ .oc-slider:focus-visible::-moz-range-thumb { box-shadow: 0 0 0 4px oklch(from var(--oc-ring) l c h / 0.28); }
112
+ .oc-slider:disabled { opacity: 0.5; cursor: not-allowed; }
113
+ .oc-slider--vertical { writing-mode: vertical-lr; direction: rtl; inline-size: var(--oc-slider-thumb); block-size: 10rem; }
114
+ .oc-slider--vertical::-webkit-slider-runnable-track {
115
+ background: linear-gradient(to top, var(--oc-primary) var(--oc-slider-progress), var(--oc-muted) var(--oc-slider-progress));
116
+ }
117
+
118
+ /* ===================================================================
119
+ TOGGLE — two-state button. Works with <button aria-pressed>, [data-state="on"] or <label class="oc-toggle"><input type="checkbox">.
120
+ Variants: --outline · sizes --sm · --lg. TOGGLE GROUP: .oc-toggle-group (radio inputs = single, checkbox inputs = multiple).
121
+ =================================================================== */
122
+ .oc-toggle {
123
+ position: relative;
124
+ display: inline-flex;
125
+ align-items: center;
126
+ justify-content: center;
127
+ gap: 0.5rem;
128
+ min-block-size: 2.25rem;
129
+ padding: 0 0.75rem;
130
+ font-family: inherit;
131
+ font-size: 0.875rem;
132
+ font-weight: 500;
133
+ line-height: 1;
134
+ color: var(--oc-foreground);
135
+ background: transparent;
136
+ border: 1px solid transparent;
137
+ border-radius: var(--oc-radius);
138
+ cursor: pointer;
139
+ user-select: none;
140
+ transition: background var(--oc-transition-fast), color var(--oc-transition-fast), border-color var(--oc-transition-fast);
141
+ }
142
+ .oc-toggle:hover { background: var(--oc-muted); }
143
+ .oc-toggle:focus-visible,
144
+ .oc-toggle:has(input:focus-visible) { outline: 2px solid var(--oc-ring); outline-offset: 1px; }
145
+ .oc-toggle[aria-pressed="true"],
146
+ .oc-toggle[data-state="on"],
147
+ .oc-toggle:has(input:checked) {
148
+ background: color-mix(in oklab, var(--oc-primary) 16%, transparent);
149
+ color: var(--oc-foreground);
150
+ }
151
+ .oc-toggle[disabled],
152
+ .oc-toggle:has(input:disabled) { opacity: 0.5; pointer-events: none; }
153
+ .oc-toggle input { position: absolute; inset: 0; margin: 0; opacity: 0; pointer-events: none; }
154
+ .oc-toggle svg { inline-size: 1rem; block-size: 1rem; flex: none; }
155
+ .oc-toggle--outline { border-color: var(--oc-input); }
156
+ .oc-toggle--outline:hover { background: var(--oc-accent); }
157
+ .oc-toggle--sm { min-block-size: 1.875rem; padding: 0 0.5rem; font-size: 0.8125rem; }
158
+ .oc-toggle--lg { min-block-size: 2.75rem; padding: 0 1rem; font-size: 1rem; }
159
+ .oc-toggle--icon { inline-size: 2.25rem; padding: 0; }
160
+
161
+ .oc-toggle-group { display: inline-flex; align-items: center; }
162
+ .oc-toggle-group > .oc-toggle { border-radius: 0; }
163
+ .oc-toggle-group > .oc-toggle:first-child { border-start-start-radius: var(--oc-radius); border-end-start-radius: var(--oc-radius); }
164
+ .oc-toggle-group > .oc-toggle:last-child { border-start-end-radius: var(--oc-radius); border-end-end-radius: var(--oc-radius); }
165
+ .oc-toggle-group > .oc-toggle--outline + .oc-toggle--outline { margin-inline-start: -1px; }
166
+ .oc-toggle-group > .oc-toggle--outline[aria-pressed="true"],
167
+ .oc-toggle-group > .oc-toggle--outline:has(input:checked) { z-index: 1; border-color: var(--oc-primary); }
168
+ .oc-toggle-group--spaced { gap: 0.25rem; }
169
+ .oc-toggle-group--spaced > .oc-toggle { border-radius: var(--oc-radius); }
170
+ .oc-toggle-group--spaced > .oc-toggle--outline + .oc-toggle--outline { margin-inline-start: 0; }
171
+
172
+ /* ===================================================================
173
+ ITEM — media + content + actions row (settings rows, file lists, search results). Use <div>, <a> or <li>.
174
+ Variants: --outline · --muted · --sm. Parts: -media (--icon · --image) · -content · -title · -description · -actions · -header · -footer
175
+ =================================================================== */
176
+ .oc-item {
177
+ display: flex;
178
+ flex-wrap: wrap;
179
+ align-items: center;
180
+ gap: 1rem;
181
+ padding: 0.875rem 1rem;
182
+ font-size: 0.875rem;
183
+ color: var(--oc-foreground);
184
+ text-decoration: none;
185
+ background: transparent;
186
+ border: 1px solid transparent;
187
+ border-radius: var(--oc-radius);
188
+ transition: background var(--oc-transition-fast), border-color var(--oc-transition-fast);
189
+ }
190
+ a.oc-item:hover,
191
+ .oc-item--interactive:hover { background: var(--oc-surface-strong); }
192
+ a.oc-item:focus-visible { outline: 2px solid var(--oc-ring); outline-offset: 1px; }
193
+ .oc-item--outline { border-color: var(--oc-border); }
194
+ .oc-item--muted { background: var(--oc-surface); }
195
+ .oc-item--sm { gap: 0.625rem; padding: 0.5rem 0.75rem; }
196
+ .oc-item-media { display: flex; flex: none; align-items: center; justify-content: center; align-self: flex-start; gap: 0.5rem; }
197
+ .oc-item-media--icon {
198
+ inline-size: 2.25rem;
199
+ block-size: 2.25rem;
200
+ color: var(--oc-foreground);
201
+ background: var(--oc-muted);
202
+ border: 1px solid var(--oc-border);
203
+ border-radius: calc(var(--oc-radius) - 2px);
204
+ }
205
+ .oc-item-media--icon svg { inline-size: 1.1rem; block-size: 1.1rem; }
206
+ .oc-item-media--image { inline-size: 2.5rem; block-size: 2.5rem; overflow: hidden; border-radius: calc(var(--oc-radius) - 2px); }
207
+ .oc-item-media--image > img { inline-size: 100%; block-size: 100%; object-fit: cover; }
208
+ .oc-item-content { display: flex; flex: 1 1 14rem; flex-direction: column; gap: 0.125rem; min-inline-size: 0; }
209
+ .oc-item-title { margin: 0; font-weight: 500; line-height: 1.35; color: var(--oc-foreground); }
210
+ .oc-item-description { margin: 0; font-size: 0.8125rem; line-height: 1.45; color: var(--oc-muted-foreground); text-wrap: balance; }
211
+ .oc-item-actions { display: flex; flex: none; align-items: center; gap: 0.5rem; }
212
+ .oc-item-header,
213
+ .oc-item-footer { flex-basis: 100%; display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; }
214
+ .oc-item-group { display: flex; flex-direction: column; }
215
+ .oc-item-group > .oc-item-separator { block-size: 1px; margin: 0; background: var(--oc-border); border: 0; }
216
+
217
+ /* ===================================================================
218
+ ASPECT RATIO — box that keeps a ratio; child img/video/iframe fills it. Custom: style="--oc-aspect: 5 / 2"
219
+ =================================================================== */
220
+ .oc-aspect {
221
+ --oc-aspect: 16 / 9;
222
+ position: relative;
223
+ overflow: hidden;
224
+ aspect-ratio: var(--oc-aspect);
225
+ background: var(--oc-muted);
226
+ border-radius: var(--oc-radius);
227
+ }
228
+ .oc-aspect--square { --oc-aspect: 1 / 1; }
229
+ .oc-aspect--photo { --oc-aspect: 4 / 3; }
230
+ .oc-aspect--portrait { --oc-aspect: 3 / 4; }
231
+ .oc-aspect--wide { --oc-aspect: 21 / 9; }
232
+ .oc-aspect > img,
233
+ .oc-aspect > video,
234
+ .oc-aspect > iframe,
235
+ .oc-aspect > svg,
236
+ .oc-aspect > picture { display: block; inline-size: 100%; block-size: 100%; border: 0; object-fit: cover; }
237
+ .oc-aspect--center { display: grid; place-items: center; }
238
+
239
+ /* ===================================================================
240
+ HOVER CARD — rich preview on hover and keyboard focus. One markup, two engines:
241
+ - a Chromium that supports `interestfor` opens the [popover] content as a real popover (top layer, hover delay by the browser)
242
+ - everywhere else :hover / :focus-within on the wrapper shows it (CSS only).
243
+ Anchor positioning (anchor() insets) keeps the card next to the trigger; anchor-scope makes several cards on one page independent.
244
+ =================================================================== */
245
+ .oc-hover-card { position: relative; display: inline-block; anchor-scope: --oc-hover-card; }
246
+ .oc-hover-card-trigger { anchor-name: --oc-hover-card; }
247
+ .oc-hover-card-content {
248
+ position: fixed;
249
+ inset: auto;
250
+ position-anchor: --oc-hover-card;
251
+ inset-block-start: anchor(bottom);
252
+ inset-inline-start: anchor(start);
253
+ z-index: 100;
254
+ display: block;
255
+ inline-size: 20rem;
256
+ max-inline-size: calc(100vw - 1rem);
257
+ margin: 0.5rem 0 0;
258
+ padding: 1rem;
259
+ font-size: 0.875rem;
260
+ color: var(--oc-popover-foreground);
261
+ background: var(--oc-popover);
262
+ border: 1px solid var(--oc-border);
263
+ border-radius: calc(var(--oc-radius) + 2px);
264
+ box-shadow: 0 8px 24px oklch(from var(--color-black-60) l c h / 0.15);
265
+ visibility: hidden;
266
+ opacity: 0;
267
+ translate: 0 -2px;
268
+ transition: opacity var(--oc-transition-fast) 0s, translate var(--oc-transition-fast) 0s, visibility 0s var(--oc-transition-fast);
269
+ }
270
+ /* invisible bridge so the pointer can travel from trigger to card */
271
+ .oc-hover-card-content::before { content: ""; position: absolute; inset-inline: 0; inset-block-end: 100%; block-size: 0.75rem; }
272
+ .oc-hover-card:hover > .oc-hover-card-content,
273
+ .oc-hover-card:focus-within > .oc-hover-card-content,
274
+ .oc-hover-card-content:popover-open {
275
+ visibility: visible;
276
+ opacity: 1;
277
+ translate: 0 0;
278
+ transition-delay: 0.35s, 0.35s, 0.35s;
279
+ }
280
+ /* flipping at screen edges only for the popover engine (fresh layout on open); the hover fallback keeps its side */
281
+ .oc-hover-card-content:popover-open { position-try-fallbacks: flip-block, flip-inline; }
282
+ .oc-hover-card--top > .oc-hover-card-content { inset-block-start: auto; inset-block-end: anchor(top); margin: 0 0 0.5rem; }
283
+ .oc-hover-card--top > .oc-hover-card-content::before { inset-block: 100% auto; }
284
+ .oc-hover-card-content--sm { inline-size: 14rem; padding: 0.75rem; }
285
+ @media (prefers-reduced-motion: reduce) {
286
+ .oc-hover-card-content { transition: none; }
287
+ }
288
+
289
+ /* ===================================================================
290
+ MENU EXTRAS (dropdown, menubar, context menu): sub-menus, checkbox/radio items, shortcuts, destructive/disabled items
291
+ Sub-menu: .oc-dropdown-sub > button.oc-dropdown-item + .oc-dropdown-submenu (opens on hover and focus-within)
292
+ Check/radio: .oc-dropdown-item + role="menuitemcheckbox|menuitemradio" + aria-checked="true|false"
293
+ =================================================================== */
294
+ .oc-dropdown-item { position: relative; }
295
+ .oc-dropdown-menu[popover] { overflow: visible; } /* the sub-menus are positioned outside the panel */
296
+ .oc-dropdown-shortcut { margin-inline-start: auto; padding-inline-start: 1.5rem; font-size: 0.7rem; letter-spacing: 0.06em; color: var(--oc-muted-foreground); }
297
+ .oc-dropdown-item[aria-disabled="true"],
298
+ .oc-dropdown-item:disabled { opacity: 0.5; pointer-events: none; }
299
+ .oc-dropdown-item--destructive { color: var(--oc-destructive); }
300
+ .oc-dropdown-item--destructive:hover { background: oklch(from var(--oc-destructive) l c h / 0.12); color: var(--oc-destructive); }
301
+ .oc-dropdown-item--inset,
302
+ .oc-dropdown-item[role="menuitemcheckbox"],
303
+ .oc-dropdown-item[role="menuitemradio"] { padding-inline-start: 2rem; }
304
+ .oc-dropdown-item[role="menuitemcheckbox"]::before,
305
+ .oc-dropdown-item[role="menuitemradio"]::before {
306
+ content: "";
307
+ position: absolute;
308
+ inset-inline-start: 0.7rem;
309
+ inset-block-start: 50%;
310
+ inline-size: 0.75rem;
311
+ block-size: 0.75rem;
312
+ translate: 0 -50%;
313
+ opacity: 0;
314
+ }
315
+ .oc-dropdown-item[role="menuitemcheckbox"]::before {
316
+ inline-size: 0.35rem;
317
+ block-size: 0.65rem;
318
+ margin-inline-start: 0.12rem;
319
+ border-inline-end: 2px solid currentColor;
320
+ border-block-end: 2px solid currentColor;
321
+ translate: 0 -62%;
322
+ rotate: 45deg;
323
+ }
324
+ .oc-dropdown-item[role="menuitemradio"]::before { inline-size: 0.45rem; block-size: 0.45rem; margin-inline-start: 0.15rem; border-radius: 50%; background: currentColor; }
325
+ .oc-dropdown-item[aria-checked="true"]::before { opacity: 1; }
326
+
327
+ .oc-dropdown-sub { position: relative; }
328
+ .oc-dropdown-sub > .oc-dropdown-item::after {
329
+ content: "";
330
+ margin-inline-start: auto;
331
+ inline-size: 0.4rem;
332
+ block-size: 0.4rem;
333
+ border-inline-end: 2px solid currentColor;
334
+ border-block-start: 2px solid currentColor;
335
+ rotate: 45deg;
336
+ opacity: 0.7;
337
+ }
338
+ .oc-dropdown-submenu {
339
+ position: absolute;
340
+ inset-block-start: -5px;
341
+ inset-inline-start: calc(100% + 2px);
342
+ z-index: 1;
343
+ min-inline-size: 11rem;
344
+ padding: 4px;
345
+ background: var(--oc-popover);
346
+ color: var(--oc-popover-foreground);
347
+ border: 1px solid var(--oc-border);
348
+ border-radius: var(--oc-radius);
349
+ box-shadow: 0 8px 24px oklch(from var(--color-black-60) l c h / 0.15);
350
+ visibility: hidden;
351
+ opacity: 0;
352
+ translate: -4px 0;
353
+ transition: opacity var(--oc-transition-fast), translate var(--oc-transition-fast), visibility 0s var(--oc-transition-fast);
354
+ }
355
+ .oc-dropdown-sub:hover > .oc-dropdown-submenu,
356
+ .oc-dropdown-sub:focus-within > .oc-dropdown-submenu { visibility: visible; opacity: 1; translate: 0 0; transition-delay: 0s; }
357
+ .oc-dropdown-sub:hover > .oc-dropdown-item,
358
+ .oc-dropdown-sub:focus-within > .oc-dropdown-item { background: var(--oc-accent); }
359
+
360
+ /* ===================================================================
361
+ MENUBAR — horizontal bar of menus. Each trigger is a button with popovertarget; each menu is a
362
+ <div class="oc-dropdown-menu" popover> (so sub-menus, check items and shortcuts work unchanged).
363
+ =================================================================== */
364
+ .oc-menubar {
365
+ display: inline-flex;
366
+ align-items: center;
367
+ gap: 0.25rem;
368
+ padding: 0.25rem;
369
+ background: var(--oc-background);
370
+ border: 1px solid var(--oc-border);
371
+ border-radius: calc(var(--oc-radius) + 2px);
372
+ }
373
+ .oc-menubar-trigger {
374
+ padding: 0.3rem 0.7rem;
375
+ font-family: inherit;
376
+ font-size: 0.875rem;
377
+ font-weight: 500;
378
+ line-height: 1.4;
379
+ color: var(--oc-foreground);
380
+ background: transparent;
381
+ border: 0;
382
+ border-radius: calc(var(--oc-radius) - 2px);
383
+ cursor: pointer;
384
+ }
385
+ .oc-menubar-trigger:hover,
386
+ .oc-menubar-trigger:focus-visible,
387
+ .oc-menubar-trigger[aria-expanded="true"],
388
+ .oc-menubar-trigger:has(+ :popover-open),
389
+ .oc-menubar-trigger:popover-open { background: var(--oc-accent); outline: none; }
390
+ .oc-menubar-trigger:focus-visible { outline: 2px solid var(--oc-ring); outline-offset: -2px; }
391
+
392
+ /* ===================================================================
393
+ CONTEXT MENU — a dropdown menu opened at the pointer. Markup: <div class="oc-dropdown-menu oc-context-menu" popover role="menu">.
394
+ Optional script (documented): on `contextmenu` call preventDefault(), set --oc-context-x / --oc-context-y, then showPopover().
395
+ Area: .oc-context-area gives the right-click target a dashed "drop zone" look.
396
+ =================================================================== */
397
+ .oc-dropdown-menu.oc-context-menu[popover] {
398
+ position: fixed;
399
+ position-area: none;
400
+ position-try-fallbacks: none;
401
+ inset: var(--oc-context-y, 0px) auto auto var(--oc-context-x, 0px);
402
+ margin: 0;
403
+ }
404
+ .oc-context-area {
405
+ display: grid;
406
+ place-items: center;
407
+ min-block-size: 8rem;
408
+ padding: 1rem;
409
+ font-size: 0.875rem;
410
+ color: var(--oc-muted-foreground);
411
+ border: 1px dashed var(--oc-border-strong);
412
+ border-radius: var(--oc-radius);
413
+ user-select: none;
414
+ }
415
+
416
+ /* ===================================================================
417
+ CAROUSEL — CSS scroll-snap track. Where supported (Chromium 135+) the browser draws ::scroll-button() arrows and
418
+ ::scroll-marker dots; elsewhere .oc-carousel-dots (anchor links) are the fallback. No JavaScript.
419
+ Markup: .oc-carousel > .oc-carousel-viewport (tabindex=0, role=region) > .oc-carousel-item + optional .oc-carousel-dots
420
+ Modifiers: --thirds (3 per view) · --halves (2 per view) · --vertical is not supported (use a list).
421
+ =================================================================== */
422
+ .oc-carousel { position: relative; anchor-scope: --oc-carousel; }
423
+ .oc-carousel-viewport {
424
+ anchor-name: --oc-carousel;
425
+ display: flex;
426
+ gap: 1rem;
427
+ overflow-x: auto;
428
+ overscroll-behavior-x: contain;
429
+ scroll-snap-type: x mandatory;
430
+ scrollbar-width: none;
431
+ border-radius: var(--oc-radius);
432
+ }
433
+ .oc-carousel-viewport::-webkit-scrollbar { display: none; }
434
+ .oc-carousel-viewport:focus-visible { outline: 2px solid var(--oc-ring); outline-offset: 2px; }
435
+ @media (prefers-reduced-motion: no-preference) {
436
+ .oc-carousel-viewport { scroll-behavior: smooth; }
437
+ }
438
+ .oc-carousel-item { flex: 0 0 100%; min-inline-size: 0; scroll-snap-align: center; }
439
+ .oc-carousel--halves .oc-carousel-item { flex-basis: calc((100% - 1rem) / 2); }
440
+ .oc-carousel--thirds .oc-carousel-item { flex-basis: calc((100% - 2rem) / 3); }
441
+ .oc-carousel-dots { display: flex; justify-content: center; gap: 0.5rem; padding-block-start: 0.75rem; }
442
+ .oc-carousel-dot {
443
+ inline-size: 0.5rem;
444
+ block-size: 0.5rem;
445
+ border-radius: 50%;
446
+ background: var(--oc-border-strong);
447
+ transition: background var(--oc-transition-fast);
448
+ }
449
+ .oc-carousel-dot:hover,
450
+ .oc-carousel-dot:focus-visible { background: var(--oc-primary); }
451
+ @supports (scroll-marker-group: after) {
452
+ .oc-carousel-dots { display: none; }
453
+ .oc-carousel-viewport { scroll-marker-group: after; }
454
+ .oc-carousel { padding-block-end: 1.5rem; }
455
+ .oc-carousel-viewport::scroll-marker-group {
456
+ position: fixed;
457
+ position-anchor: --oc-carousel;
458
+ inset-inline-start: anchor(start);
459
+ inset-block-start: calc(anchor(end) + 0.5rem);
460
+ inline-size: anchor-size(inline);
461
+ display: flex;
462
+ justify-content: center;
463
+ gap: 0.5rem;
464
+ }
465
+ .oc-carousel-item::scroll-marker {
466
+ content: "";
467
+ inline-size: 0.5rem;
468
+ block-size: 0.5rem;
469
+ border-radius: 999px;
470
+ background: var(--oc-border-strong);
471
+ transition: inline-size var(--oc-transition-fast), background var(--oc-transition-fast);
472
+ }
473
+ .oc-carousel-item::scroll-marker:target-current { inline-size: 1.25rem; background: var(--oc-primary); }
474
+ .oc-carousel-item::scroll-marker:focus-visible { outline: 2px solid var(--oc-ring); outline-offset: 2px; }
475
+ }
476
+ @supports selector(::scroll-button(inline-start)) {
477
+ .oc-carousel-viewport::scroll-button(inline-start),
478
+ .oc-carousel-viewport::scroll-button(inline-end) {
479
+ position: fixed;
480
+ position-anchor: --oc-carousel;
481
+ inset-block-start: anchor(center);
482
+ translate: 0 -50%;
483
+ inline-size: 2.25rem;
484
+ block-size: 2.25rem;
485
+ display: grid;
486
+ place-items: center;
487
+ padding: 0;
488
+ font-size: 1rem;
489
+ color: var(--oc-foreground);
490
+ background: var(--oc-background);
491
+ border: 1px solid var(--oc-border);
492
+ border-radius: 50%;
493
+ box-shadow: 0 2px 8px oklch(from var(--color-black-60) l c h / 0.18);
494
+ cursor: pointer;
495
+ }
496
+ .oc-carousel-viewport::scroll-button(inline-start) { content: "\2190" / "Previous slide"; inset-inline-start: calc(anchor(start) + 0.5rem); }
497
+ .oc-carousel-viewport::scroll-button(inline-end) { content: "\2192" / "Next slide"; inset-inline-end: calc(anchor(end) + 0.5rem); }
498
+ .oc-carousel-viewport::scroll-button(inline-start):hover,
499
+ .oc-carousel-viewport::scroll-button(inline-end):hover { background: var(--oc-accent); }
500
+ .oc-carousel-viewport::scroll-button(inline-start):focus-visible,
501
+ .oc-carousel-viewport::scroll-button(inline-end):focus-visible { outline: 2px solid var(--oc-ring); outline-offset: 2px; }
502
+ .oc-carousel-viewport::scroll-button(inline-start):disabled,
503
+ .oc-carousel-viewport::scroll-button(inline-end):disabled { opacity: 0.35; cursor: default; }
504
+ }
505
+
506
+ /* ===================================================================
507
+ RESIZABLE — split panes using the native CSS `resize` property (drag the grip in the corner of the resizable panel).
508
+ A full-length drag handle between panes with keyboard support needs script: wrapper responsibility.
509
+ Markup: .oc-resizable > .oc-resizable-panel.oc-resizable-panel--resizable + .oc-resizable-panel. Modifier: --vertical.
510
+ =================================================================== */
511
+ .oc-resizable {
512
+ display: flex;
513
+ min-block-size: 10rem;
514
+ overflow: hidden;
515
+ font-size: 0.875rem;
516
+ background: var(--oc-card);
517
+ border: 1px solid var(--oc-border);
518
+ border-radius: var(--oc-radius);
519
+ }
520
+ .oc-resizable-panel { flex: 1 1 0; min-inline-size: 0; min-block-size: 0; padding: 1rem; overflow: auto; }
521
+ .oc-resizable-panel--resizable {
522
+ flex: 0 0 auto;
523
+ inline-size: 50%;
524
+ min-inline-size: 5rem;
525
+ max-inline-size: calc(100% - 5rem);
526
+ resize: horizontal;
527
+ border-inline-end: 1px solid var(--oc-border-strong);
528
+ }
529
+ .oc-resizable-panel--resizable::-webkit-resizer { background: linear-gradient(135deg, transparent 50%, var(--oc-border-strong) 50% 56%, transparent 56% 70%, var(--oc-border-strong) 70% 76%, transparent 76%); }
530
+ .oc-resizable--vertical { flex-direction: column; }
531
+ .oc-resizable--vertical > .oc-resizable-panel--resizable {
532
+ inline-size: auto;
533
+ min-inline-size: 0;
534
+ max-inline-size: none;
535
+ block-size: 50%;
536
+ min-block-size: 3rem;
537
+ max-block-size: calc(100% - 3rem);
538
+ resize: vertical;
539
+ border-inline-end: 0;
540
+ border-block-end: 1px solid var(--oc-border-strong);
541
+ }
542
+ .oc-resizable-box { resize: both; overflow: auto; min-inline-size: 8rem; min-block-size: 4rem; max-inline-size: 100%; }
543
+
544
+ /* ===================================================================
545
+ COMBOBOX — input + listbox. CSS gives the look and opens the list while the wrapper has focus (or [data-open]).
546
+ The wrapper script owns: filtering, aria-activedescendant, aria-selected, writing the chosen value into the input.
547
+ Markup: .oc-combobox > input.oc-combobox-input[role=combobox] + ul.oc-combobox-list[role=listbox] > li.oc-combobox-option[role=option][tabindex=-1]
548
+ =================================================================== */
549
+ .oc-combobox { position: relative; display: inline-block; inline-size: min(100%, 18rem); }
550
+ .oc-combobox-input { padding-inline-end: 2.25rem; }
551
+ .oc-combobox::after {
552
+ content: "";
553
+ position: absolute;
554
+ inset-block-start: 1.05rem;
555
+ inset-inline-end: 0.9rem;
556
+ inline-size: 0.4rem;
557
+ block-size: 0.4rem;
558
+ border-inline-end: 2px solid var(--oc-muted-foreground);
559
+ border-block-end: 2px solid var(--oc-muted-foreground);
560
+ rotate: 45deg;
561
+ pointer-events: none;
562
+ transition: rotate var(--oc-transition-fast);
563
+ }
564
+ .oc-combobox:focus-within::after,
565
+ .oc-combobox[data-open]::after { rotate: 225deg; translate: 0 3px; }
566
+ .oc-combobox-list {
567
+ position: absolute;
568
+ inset-inline: 0;
569
+ inset-block-start: calc(100% + 4px);
570
+ z-index: 100;
571
+ max-block-size: 15rem;
572
+ margin: 0;
573
+ padding: 4px;
574
+ overflow-y: auto;
575
+ list-style: none;
576
+ font-size: 0.875rem;
577
+ color: var(--oc-popover-foreground);
578
+ background: var(--oc-popover);
579
+ border: 1px solid var(--oc-border);
580
+ border-radius: var(--oc-radius);
581
+ box-shadow: 0 8px 24px oklch(from var(--color-black-60) l c h / 0.15);
582
+ visibility: hidden;
583
+ opacity: 0;
584
+ translate: 0 -4px;
585
+ transition: opacity var(--oc-transition-fast), translate var(--oc-transition-fast), visibility 0s var(--oc-transition-fast);
586
+ }
587
+ .oc-combobox:focus-within > .oc-combobox-list,
588
+ .oc-combobox[data-open] > .oc-combobox-list { visibility: visible; opacity: 1; translate: 0 0; transition-delay: 0s; }
589
+ .oc-combobox[data-open="false"] > .oc-combobox-list { visibility: hidden; opacity: 0; }
590
+ .oc-combobox-option {
591
+ display: flex;
592
+ align-items: center;
593
+ justify-content: space-between;
594
+ gap: 0.5rem;
595
+ padding: 0.45rem 0.625rem;
596
+ border-radius: calc(var(--oc-radius) - 2px);
597
+ cursor: pointer;
598
+ outline: none;
599
+ }
600
+ .oc-combobox-option:hover,
601
+ .oc-combobox-option:focus-visible,
602
+ .oc-combobox-option[data-active] { background: var(--oc-accent); color: var(--oc-accent-foreground); }
603
+ .oc-combobox-option[aria-selected="true"]::after {
604
+ content: "";
605
+ inline-size: 0.35rem;
606
+ block-size: 0.65rem;
607
+ border-inline-end: 2px solid currentColor;
608
+ border-block-end: 2px solid currentColor;
609
+ rotate: 45deg;
610
+ margin-inline-end: 0.2rem;
611
+ }
612
+ .oc-combobox-option[aria-disabled="true"] { opacity: 0.5; pointer-events: none; }
613
+ .oc-combobox-option[hidden],
614
+ .oc-combobox-empty[hidden] { display: none; }
615
+ .oc-combobox-group { padding: 0.5rem 0.625rem 0.25rem; font-size: 0.7rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; color: var(--oc-muted-foreground); }
616
+ .oc-combobox-empty { padding: 1rem; text-align: center; color: var(--oc-muted-foreground); }
617
+
618
+ /* ===================================================================
619
+ DRAWER — bottom sheet on <dialog> with a drag-handle look. Open: <button command="show-modal" commandfor="id">.
620
+ Dismiss: command="close", Esc or closedby="any" (tap outside). Swipe-to-dismiss is not built in (needs script).
621
+ =================================================================== */
622
+ dialog.oc-drawer {
623
+ display: none;
624
+ inline-size: 100%;
625
+ block-size: 100%;
626
+ max-inline-size: none;
627
+ max-block-size: none;
628
+ margin: 0;
629
+ padding: 0;
630
+ border: 0;
631
+ background: transparent;
632
+ color: var(--oc-foreground);
633
+ }
634
+ dialog.oc-drawer[open] { display: flex; align-items: flex-end; justify-content: center; }
635
+ dialog.oc-drawer::backdrop { background: oklch(from var(--color-black-60) l c h / 0.5); backdrop-filter: blur(4px); }
636
+ .oc-drawer-content {
637
+ display: flex;
638
+ flex-direction: column;
639
+ inline-size: min(100%, 32rem);
640
+ max-block-size: 85dvh;
641
+ background: var(--oc-background);
642
+ border: 1px solid var(--oc-border);
643
+ border-block-end: 0;
644
+ border-radius: calc(var(--oc-radius) * 2) calc(var(--oc-radius) * 2) 0 0;
645
+ box-shadow: 0 -16px 48px oklch(from var(--color-black-60) l c h / 0.2);
646
+ }
647
+ dialog.oc-drawer[open] .oc-drawer-content {
648
+ transition: translate var(--oc-transition-normal), opacity var(--oc-transition-normal);
649
+ @starting-style { translate: 0 100%; opacity: 0; }
650
+ }
651
+ .oc-drawer-handle { flex: none; inline-size: 3rem; block-size: 0.3rem; margin: 0.625rem auto 0; background: var(--oc-border-strong); border-radius: 999px; }
652
+ .oc-drawer-header { display: flex; flex-direction: column; gap: 0.25rem; padding: 1rem 1.5rem 0; text-align: center; }
653
+ .oc-drawer-title { margin: 0; font-size: 1.05rem; font-weight: 600; color: var(--oc-foreground); }
654
+ .oc-drawer-description { margin: 0; font-size: 0.875rem; color: var(--oc-muted-foreground); }
655
+ .oc-drawer-body { flex: 1; min-block-size: 0; padding: 1rem 1.5rem; overflow-y: auto; font-size: 0.875rem; }
656
+ .oc-drawer-footer { display: flex; flex-direction: column; gap: 0.5rem; padding: 1rem 1.5rem 1.5rem; }
657
+ @media (min-width: 40rem) {
658
+ dialog.oc-drawer--center[open] { align-items: center; }
659
+ dialog.oc-drawer--center .oc-drawer-content { border-block-end: 1px solid var(--oc-border); border-radius: calc(var(--oc-radius) * 2); }
660
+ dialog.oc-drawer--center .oc-drawer-handle { display: none; }
661
+ }
662
+
663
+ /* ===================================================================
664
+ NATIVE SELECT — the real <select>. .oc-select (components.css) styles the closed control everywhere.
665
+ Add .oc-select--custom where `appearance: base-select` exists to also style the picker popup (rounded, themed, checkmark).
666
+ =================================================================== */
667
+ @supports (appearance: base-select) {
668
+ .oc-select--custom,
669
+ .oc-select--custom::picker(select) { appearance: base-select; }
670
+ /* One row like the closed .oc-select: text left, icon right, full width of the field
671
+ (display: block put the icon on a second line and base-select shrank to the text). */
672
+ .oc-select--custom { display: flex; align-items: center; gap: 0.5rem; inline-size: 100%; field-sizing: fixed; background-image: none; padding-inline-end: 0.75rem; }
673
+ .oc-select--custom::picker-icon { margin-inline-start: auto; color: var(--oc-muted-foreground); transition: rotate var(--oc-transition-fast); }
674
+ .oc-select--custom:open::picker-icon { rotate: 180deg; }
675
+ .oc-select--custom::picker(select) {
676
+ padding: 4px;
677
+ margin-block: 4px;
678
+ color: var(--oc-popover-foreground);
679
+ background: var(--oc-popover);
680
+ border: 1px solid var(--oc-border);
681
+ border-radius: var(--oc-radius);
682
+ box-shadow: 0 8px 24px oklch(from var(--color-black-60) l c h / 0.15);
683
+ transition: opacity var(--oc-transition-fast), display var(--oc-transition-fast) allow-discrete, overlay var(--oc-transition-fast) allow-discrete;
684
+ }
685
+ .oc-select--custom:open::picker(select) { opacity: 1; @starting-style { opacity: 0; } }
686
+ .oc-select--custom option { display: flex; align-items: center; gap: 0.5rem; padding: 0.45rem 0.625rem; border-radius: calc(var(--oc-radius) - 2px); }
687
+ .oc-select--custom option:hover,
688
+ .oc-select--custom option:focus-visible { background: var(--oc-accent); color: var(--oc-accent-foreground); outline: none; }
689
+ .oc-select--custom option:checked { font-weight: 600; }
690
+ .oc-select--custom option::checkmark { order: 1; margin-inline-start: auto; color: var(--oc-primary); }
691
+ }
692
+
693
+ /* ===================================================================
694
+ TOAST STACK — stacked toasts (newest in front, older ones peek out above). Hover or focus expands the stack.
695
+ Add .oc-toast-container--stack to .oc-toast-container; put the NEWEST toast LAST. Up to 4 visible layers.
696
+ =================================================================== */
697
+ .oc-toast-container--stack { display: grid; align-items: end; gap: 0; padding-block-start: 1.5rem; pointer-events: none; }
698
+ .oc-toast-container--stack > .oc-toast {
699
+ --oc-stack-depth: 0;
700
+ grid-area: 1 / 1;
701
+ transform-origin: 50% 100%;
702
+ translate: 0 calc(var(--oc-stack-depth) * -0.7rem);
703
+ scale: calc(1 - var(--oc-stack-depth) * 0.05);
704
+ opacity: calc(1 - var(--oc-stack-depth) * 0.25);
705
+ z-index: calc(10 - var(--oc-stack-depth));
706
+ transition: translate var(--oc-transition-normal), scale var(--oc-transition-normal), opacity var(--oc-transition-normal);
707
+ }
708
+ .oc-toast-container--stack > .oc-toast:nth-last-child(2) { --oc-stack-depth: 1; }
709
+ .oc-toast-container--stack > .oc-toast:nth-last-child(3) { --oc-stack-depth: 2; }
710
+ .oc-toast-container--stack > .oc-toast:nth-last-child(4) { --oc-stack-depth: 3; }
711
+ .oc-toast-container--stack > .oc-toast:nth-last-child(n + 5) { --oc-stack-depth: 4; opacity: 0; visibility: hidden; }
712
+ .oc-toast-container--stack > .oc-toast:not(:last-child) { pointer-events: none; }
713
+ .oc-toast-container--stack > .oc-toast:not(:last-child) > * { visibility: hidden; }
714
+ .oc-toast-container--stack:hover,
715
+ .oc-toast-container--stack:focus-within { display: flex; flex-direction: column; gap: 0.5rem; pointer-events: auto; }
716
+ .oc-toast-container--stack:hover > .oc-toast,
717
+ .oc-toast-container--stack:focus-within > .oc-toast { translate: none; scale: none; opacity: 1; visibility: visible; pointer-events: auto; }
718
+ .oc-toast-container--stack:hover > .oc-toast > *,
719
+ .oc-toast-container--stack:focus-within > .oc-toast > * { visibility: visible; }
720
+ @media (prefers-reduced-motion: reduce) {
721
+ .oc-toast-container--stack > .oc-toast { transition: none; }
722
+ }
723
+
724
+ /* ===================================================================
725
+ CALENDAR — month view markup (table role="grid") styled as a calendar. Markup only: the month math and selection
726
+ state (aria-selected, --range-*) come from your code; a native <input type="date"> remains the zero-JS option.
727
+ Parts: -header · -title · -nav · -grid · -weekday · -day (button) . Day states: [aria-selected=true] · --today · --outside · --range-start/-middle/-end · :disabled
728
+ =================================================================== */
729
+ .oc-calendar {
730
+ --oc-calendar-cell: 2.25rem;
731
+ display: inline-block;
732
+ padding: 0.75rem;
733
+ color: var(--oc-foreground);
734
+ background: var(--oc-card);
735
+ border: 1px solid var(--oc-border);
736
+ border-radius: calc(var(--oc-radius) + 2px);
737
+ }
738
+ .oc-calendar-header { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; margin-block-end: 0.5rem; }
739
+ .oc-calendar-title { margin: 0; font-size: 0.875rem; font-weight: 600; }
740
+ .oc-calendar-nav {
741
+ display: inline-grid;
742
+ place-items: center;
743
+ inline-size: 1.75rem;
744
+ block-size: 1.75rem;
745
+ padding: 0;
746
+ color: var(--oc-foreground);
747
+ background: transparent;
748
+ border: 1px solid var(--oc-input);
749
+ border-radius: calc(var(--oc-radius) - 2px);
750
+ cursor: pointer;
751
+ }
752
+ .oc-calendar-nav:hover { background: var(--oc-accent); }
753
+ .oc-calendar-nav:focus-visible { outline: 2px solid var(--oc-ring); outline-offset: 1px; }
754
+ .oc-calendar-grid { border-collapse: collapse; table-layout: fixed; }
755
+ .oc-calendar-grid th,
756
+ .oc-calendar-grid td { padding: 0; text-align: center; }
757
+ .oc-calendar-weekday { inline-size: var(--oc-calendar-cell); padding-block-end: 0.25rem; font-size: 0.75rem; font-weight: 500; color: var(--oc-muted-foreground); }
758
+ .oc-calendar-day {
759
+ display: inline-grid;
760
+ place-items: center;
761
+ inline-size: var(--oc-calendar-cell);
762
+ block-size: var(--oc-calendar-cell);
763
+ padding: 0;
764
+ font-family: inherit;
765
+ font-size: 0.875rem;
766
+ font-weight: 400;
767
+ line-height: 1;
768
+ color: var(--oc-foreground);
769
+ background: transparent;
770
+ border: 0;
771
+ border-radius: calc(var(--oc-radius) - 2px);
772
+ cursor: pointer;
773
+ font-variant-numeric: tabular-nums;
774
+ }
775
+ .oc-calendar-day:hover { background: var(--oc-accent); }
776
+ .oc-calendar-day:focus-visible { outline: 2px solid var(--oc-ring); outline-offset: -2px; }
777
+ .oc-calendar-day--today { background: var(--oc-muted); font-weight: 600; }
778
+ .oc-calendar-day--outside { color: var(--oc-muted-foreground); opacity: 0.55; }
779
+ .oc-calendar-day:disabled { opacity: 0.35; pointer-events: none; }
780
+ .oc-calendar-day[aria-selected="true"],
781
+ .oc-calendar-day--range-start,
782
+ .oc-calendar-day--range-end { background: var(--oc-primary); color: var(--oc-primary-foreground); font-weight: 600; opacity: 1; }
783
+ .oc-calendar-day--range-middle { background: color-mix(in oklab, var(--oc-primary) 16%, transparent); border-radius: 0; }
784
+ .oc-calendar-day--range-start { border-start-end-radius: 0; border-end-end-radius: 0; }
785
+ .oc-calendar-day--range-end { border-start-start-radius: 0; border-end-start-radius: 0; }
786
+
787
+ /* ===================================================================
788
+ DATE PICKER — button trigger + popover with a calendar. The popover modifier removes padding so the calendar is the panel.
789
+ Zero-JS alternative: <input class="oc-input" type="date"> (native picker; inherits color-scheme, so it follows the page theme, not the OS).
790
+ =================================================================== */
791
+ .oc-popover--calendar[popover] { inline-size: auto; padding: 0; border: 0; background: transparent; box-shadow: none; }
792
+ .oc-popover--calendar .oc-calendar { box-shadow: 0 8px 24px oklch(from var(--color-black-60) l c h / 0.15); }
793
+ .oc-date-picker-trigger { justify-content: space-between; min-inline-size: 14rem; font-weight: 400; }
794
+ .oc-date-picker-trigger[data-empty] { color: var(--oc-muted-foreground); }
795
+ input.oc-input[type="date"],
796
+ input.oc-input[type="time"],
797
+ input.oc-input[type="datetime-local"],
798
+ input.oc-input[type="month"] { inline-size: auto; min-inline-size: 11rem; }
799
+
800
+ /* ===================================================================
801
+ CHART TOKENS — categorical series colors from the Omnia palette (light-dark). Use as fill/stroke/background.
802
+ Helpers: .oc-chart-legend · .oc-chart-swatch · .oc-chart-bars (+ --oc-bar) · .oc-chart-pie (conic-gradient from inline stops)
803
+ Series: nth-child auto-assigns --oc-chart-color 1..5 on .oc-chart-bars > *, .oc-chart-legend > *.
804
+ =================================================================== */
805
+ :root {
806
+ --oc-chart-1: light-dark(var(--color-vividblue-95), var(--color-vividblue-50));
807
+ --oc-chart-2: light-dark(var(--color-teal-70), var(--color-teal-30));
808
+ --oc-chart-3: light-dark(var(--color-orange-90), var(--color-orange-50));
809
+ --oc-chart-4: light-dark(var(--color-purple-50), var(--color-purple-30));
810
+ --oc-chart-5: light-dark(var(--color-pink-95), var(--color-pink-50));
811
+ --oc-chart-grid: var(--oc-border);
812
+ --oc-chart-axis: var(--oc-muted-foreground);
813
+ }
814
+ .oc-chart-legend { display: flex; flex-wrap: wrap; gap: 0.5rem 1rem; margin: 0; padding: 0; list-style: none; font-size: 0.8125rem; color: var(--oc-muted-foreground); }
815
+ .oc-chart-legend > li { display: inline-flex; align-items: center; gap: 0.4rem; }
816
+ .oc-chart-swatch { inline-size: 0.65rem; block-size: 0.65rem; flex: none; border-radius: 2px; background: var(--oc-chart-color, var(--oc-chart-1)); }
817
+ .oc-chart-legend > :nth-child(1) { --oc-chart-color: var(--oc-chart-1); }
818
+ .oc-chart-legend > :nth-child(2) { --oc-chart-color: var(--oc-chart-2); }
819
+ .oc-chart-legend > :nth-child(3) { --oc-chart-color: var(--oc-chart-3); }
820
+ .oc-chart-legend > :nth-child(4) { --oc-chart-color: var(--oc-chart-4); }
821
+ .oc-chart-legend > :nth-child(5) { --oc-chart-color: var(--oc-chart-5); }
822
+ .oc-chart-bars {
823
+ display: grid;
824
+ grid-auto-flow: column;
825
+ grid-auto-columns: 1fr;
826
+ align-items: end;
827
+ gap: 0.5rem;
828
+ block-size: 10rem;
829
+ margin: 0;
830
+ padding: 0 0 0.25rem;
831
+ list-style: none;
832
+ border-block-end: 1px solid var(--oc-chart-grid);
833
+ background: repeating-linear-gradient(to top, transparent 0 calc(25% - 1px), var(--oc-chart-grid) calc(25% - 1px) 25%);
834
+ }
835
+ .oc-chart-bars > * {
836
+ --oc-bar: 50%;
837
+ block-size: var(--oc-bar);
838
+ background: var(--oc-chart-color, var(--oc-chart-1));
839
+ border-start-start-radius: 4px;
840
+ border-start-end-radius: 4px;
841
+ font-size: 0.7rem;
842
+ text-align: center;
843
+ color: var(--oc-primary-foreground);
844
+ }
845
+ .oc-chart-bars > :nth-child(5n + 1) { --oc-chart-color: var(--oc-chart-1); }
846
+ .oc-chart-bars > :nth-child(5n + 2) { --oc-chart-color: var(--oc-chart-2); }
847
+ .oc-chart-bars > :nth-child(5n + 3) { --oc-chart-color: var(--oc-chart-3); }
848
+ .oc-chart-bars > :nth-child(5n + 4) { --oc-chart-color: var(--oc-chart-4); }
849
+ .oc-chart-bars > :nth-child(5n + 5) { --oc-chart-color: var(--oc-chart-5); }
850
+ .oc-chart-pie { --oc-pie-size: 9rem; inline-size: var(--oc-pie-size); aspect-ratio: 1; border-radius: 50%; background: conic-gradient(var(--oc-chart-1) 0 40%, var(--oc-chart-2) 0 65%, var(--oc-chart-3) 0 82%, var(--oc-chart-4) 0 100%); }
851
+ .oc-chart-pie--donut { mask: radial-gradient(circle, transparent 0 52%, var(--color-black-60) 53%); }
852
+
853
+ /* ===================================================================
854
+ EXTENSIONS OF EXISTING COMPONENTS (registered in the manifest): button-group · label · radio · scroll-area · separator
855
+ =================================================================== */
856
+ .oc-btn-group > .oc-btn:focus-visible { position: relative; z-index: 1; }
857
+ .oc-btn-group--vertical { flex-direction: column; }
858
+ .oc-btn-group--vertical .oc-btn,
859
+ .oc-btn-group--vertical .oc-btn:first-child,
860
+ .oc-btn-group--vertical .oc-btn:last-child { border-radius: 0; }
861
+ .oc-btn-group--vertical .oc-btn:first-child { border-radius: var(--oc-radius) var(--oc-radius) 0 0; }
862
+ .oc-btn-group--vertical .oc-btn:last-child { border-radius: 0 0 var(--oc-radius) var(--oc-radius); }
863
+ .oc-btn-group--vertical .oc-btn + .oc-btn { margin-left: 0; margin-top: -1px; }
864
+ .oc-btn-group-text {
865
+ display: inline-flex;
866
+ align-items: center;
867
+ padding: 0 0.75rem;
868
+ font-size: 0.875rem;
869
+ color: var(--oc-muted-foreground);
870
+ background: var(--oc-muted);
871
+ border: 1px solid var(--oc-input);
872
+ margin-left: -1px;
873
+ }
874
+
875
+ .oc-label--required::after { content: " *"; color: var(--oc-destructive); }
876
+ .oc-label--inline { display: inline-flex; align-items: center; gap: 0.5rem; margin-bottom: 0; cursor: pointer; }
877
+ .oc-label--muted { font-weight: 400; color: var(--oc-muted-foreground); }
878
+
879
+ .oc-radio-group { display: flex; flex-direction: column; gap: 0.625rem; margin: 0; padding: 0; border: 0; }
880
+ .oc-radio-group--row { flex-direction: row; flex-wrap: wrap; gap: 0.625rem 1.25rem; }
881
+ .oc-radio-item { display: flex; align-items: flex-start; gap: 0.625rem; font-size: 0.875rem; color: var(--oc-foreground); cursor: pointer; }
882
+ .oc-radio-item > .oc-radio { flex: none; margin-block-start: 0.15rem; }
883
+ .oc-radio-item small { display: block; font-size: 0.8125rem; color: var(--oc-muted-foreground); }
884
+ .oc-radio-item:has(:disabled) { opacity: 0.55; cursor: not-allowed; }
885
+ .oc-radio-item--card { padding: 0.75rem 1rem; border: 1px solid var(--oc-border); border-radius: var(--oc-radius); }
886
+ .oc-radio-item--card:has(:checked) { border-color: var(--oc-primary); background: color-mix(in oklab, var(--oc-primary) 8%, transparent); }
887
+ .oc-radio-item--card:has(:focus-visible) { outline: 2px solid var(--oc-ring); outline-offset: 1px; }
888
+
889
+ .oc-scroll-area {
890
+ max-block-size: 14rem;
891
+ padding: 0.75rem 1rem;
892
+ overflow: auto;
893
+ font-size: 0.875rem;
894
+ background: var(--oc-card);
895
+ border: 1px solid var(--oc-border);
896
+ border-radius: var(--oc-radius);
897
+ scrollbar-width: thin;
898
+ scrollbar-color: var(--oc-border-strong) transparent;
899
+ }
900
+ .oc-scroll-area:focus-visible { outline: 2px solid var(--oc-ring); outline-offset: 2px; }
901
+ .oc-scroll-area--x { max-block-size: none; overflow-y: hidden; white-space: nowrap; }
902
+ /* shadows at the edges that still have content to scroll to (pure CSS, no JavaScript) */
903
+ .oc-scroll-area--shadows {
904
+ --oc-scroll-shadow: oklch(from var(--oc-foreground) l c h / 0.18);
905
+ background:
906
+ linear-gradient(var(--oc-card) 30%, transparent) center top / 100% 2rem no-repeat local,
907
+ linear-gradient(transparent, var(--oc-card) 70%) center bottom / 100% 2rem no-repeat local,
908
+ radial-gradient(farthest-side at 50% 0, var(--oc-scroll-shadow), transparent) center top / 100% 0.6rem no-repeat scroll,
909
+ radial-gradient(farthest-side at 50% 100%, var(--oc-scroll-shadow), transparent) center bottom / 100% 0.6rem no-repeat scroll,
910
+ var(--oc-card);
911
+ }
912
+
913
+ .oc-separator-text { display: flex; align-items: center; gap: 0.75rem; margin: 16px 0; font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.06em; color: var(--oc-muted-foreground); }
914
+ .oc-separator-text::before,
915
+ .oc-separator-text::after { content: ""; flex: 1; block-size: 1px; background: var(--oc-border); }
916
+ .oc-separator--strong { background: var(--oc-border-strong); }