@birdycom/omniaui 0.0.0-stage → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (216) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +20 -2
  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 +914 -0
  9. package/dist/components.css +10081 -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 +265 -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 +89 -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 +76 -0
  44. package/dist/parts/forms-number-input.css +95 -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 +75 -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 +67 -4
  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 +17 -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,90 @@
1
+ /* File: packages/ui/src/parts/media-chat-composer.css | Map: frame (focus ring) -> textarea (grows with content)
2
+ -> bar (tools | hint | send) -> tool buttons -> send button (disabled while empty) -> inline modifier
3
+ Chat composer: a message field with a toolbar and a send button, in one rounded frame.
4
+ The textarea grows with field-sizing; the send button looks disabled while the field is empty (:placeholder-shown). */
5
+
6
+ .oc-chat-composer {
7
+ display: grid;
8
+ gap: 0.25rem;
9
+ inline-size: 100%;
10
+ padding: 0.5rem;
11
+ border: 1px solid var(--oc-input);
12
+ border-radius: calc(var(--oc-radius) + 6px);
13
+ background: var(--oc-card);
14
+ color: var(--oc-foreground);
15
+ transition: border-color var(--oc-transition-fast), box-shadow var(--oc-transition-fast);
16
+ }
17
+ .oc-chat-composer:focus-within {
18
+ border-color: var(--oc-ring);
19
+ box-shadow: 0 0 0 3px color-mix(in oklch, var(--oc-ring) 20%, transparent);
20
+ }
21
+
22
+ /* The field itself has no frame: the composer is the frame. */
23
+ .oc-chat-composer__input {
24
+ inline-size: 100%;
25
+ min-block-size: 2.5rem;
26
+ max-block-size: 10rem;
27
+ padding: 0.375rem 0.5rem;
28
+ border: 0;
29
+ outline: 0;
30
+ resize: none;
31
+ background: transparent;
32
+ color: inherit;
33
+ font: inherit;
34
+ font-size: 0.9375rem;
35
+ line-height: 1.5;
36
+ field-sizing: content;
37
+ }
38
+ .oc-chat-composer__input::placeholder { color: var(--oc-muted-foreground); }
39
+
40
+ /* Toolbar: tools left, hint in the middle, send right. */
41
+ .oc-chat-composer__bar { display: flex; align-items: center; gap: 0.25rem; }
42
+ .oc-chat-composer__tools { display: flex; align-items: center; gap: 0.125rem; }
43
+ .oc-chat-composer__hint { margin-inline-start: auto; padding-inline: 0.5rem; font-size: 0.75rem; color: var(--oc-muted-foreground); }
44
+
45
+ .oc-chat-composer__tool {
46
+ display: inline-grid;
47
+ place-items: center;
48
+ inline-size: 2.25rem;
49
+ aspect-ratio: 1;
50
+ padding: 0;
51
+ border: 0;
52
+ border-radius: 50%;
53
+ background: transparent;
54
+ color: var(--oc-muted-foreground);
55
+ font-size: 1.05rem;
56
+ line-height: 1;
57
+ cursor: pointer;
58
+ }
59
+ .oc-chat-composer__tool:hover { background: var(--oc-surface-strong); color: var(--oc-foreground); }
60
+ .oc-chat-composer__tool:focus-visible { outline: 2px solid var(--oc-ring); outline-offset: 1px; }
61
+
62
+ .oc-chat-composer__send {
63
+ display: inline-grid;
64
+ place-items: center;
65
+ inline-size: 2.25rem;
66
+ aspect-ratio: 1;
67
+ margin-inline-start: auto;
68
+ padding: 0;
69
+ border: 0;
70
+ border-radius: 50%;
71
+ background: var(--oc-primary);
72
+ color: var(--oc-primary-foreground);
73
+ font-size: 1rem;
74
+ cursor: pointer;
75
+ transition: opacity var(--oc-transition-fast), transform var(--oc-transition-fast);
76
+ }
77
+ .oc-chat-composer__hint + .oc-chat-composer__send { margin-inline-start: 0; }
78
+ .oc-chat-composer__send:hover { transform: scale(1.05); }
79
+ .oc-chat-composer__send:focus-visible { outline: 2px solid var(--oc-ring); outline-offset: 2px; }
80
+ /* Empty field (placeholder visible) or a real disabled button: dimmed and not clickable. */
81
+ .oc-chat-composer:has(.oc-chat-composer__input:placeholder-shown) .oc-chat-composer__send,
82
+ .oc-chat-composer__send:disabled { opacity: 0.4; pointer-events: none; }
83
+
84
+ /* Inline: one row — tools, field, send (for compact chat panels). */
85
+ .oc-chat-composer--inline { grid-template-columns: auto minmax(0, 1fr) auto; align-items: end; padding: 0.375rem; border-radius: 1.4rem; }
86
+ .oc-chat-composer--inline .oc-chat-composer__bar { display: contents; }
87
+ .oc-chat-composer--inline .oc-chat-composer__tools { grid-column: 1; grid-row: 1; }
88
+ .oc-chat-composer--inline .oc-chat-composer__input { grid-column: 2; grid-row: 1; min-block-size: 2.25rem; padding-block: 0.4rem; }
89
+ .oc-chat-composer--inline .oc-chat-composer__send { grid-column: 3; grid-row: 1; }
90
+ .oc-chat-composer--inline .oc-chat-composer__hint { display: none; }
@@ -0,0 +1,151 @@
1
+ /* File: packages/ui/src/parts/media-doc-viewer.css | Map: grid -> card (bar, label, expand) -> frame (pdf, video, image)
2
+ -> full-screen dialog -> small screens
3
+ Document viewer: a PDF, video or image shown inline in a card with a label, plus a full-screen view on a native
4
+ <dialog> opened with command="show-modal". Based on the PDF and video viewers of the Verdavita service pages. */
5
+
6
+ /* ---- Grid of viewers: cards keep their width, the group is centred ---- */
7
+ .oc-doc-viewer-grid {
8
+ display: grid;
9
+ grid-template-columns: repeat(auto-fill, minmax(min(100%, 15rem), 1fr));
10
+ gap: 1rem 1.5rem;
11
+ inline-size: 100%;
12
+ }
13
+
14
+ /* ---- Card ---- */
15
+ .oc-doc-viewer {
16
+ display: grid;
17
+ align-content: start;
18
+ gap: 0.5rem;
19
+ min-inline-size: 0;
20
+ margin: 0;
21
+ }
22
+ .oc-doc-viewer__bar {
23
+ display: flex;
24
+ align-items: center;
25
+ justify-content: space-between;
26
+ gap: 0.5rem;
27
+ min-inline-size: 0;
28
+ }
29
+ .oc-doc-viewer__label {
30
+ flex: 1 1 auto;
31
+ min-inline-size: 0;
32
+ overflow: hidden;
33
+ color: var(--oc-foreground);
34
+ font-size: 0.875rem;
35
+ font-weight: 600;
36
+ white-space: nowrap;
37
+ text-overflow: ellipsis;
38
+ }
39
+ .oc-doc-viewer__meta { flex: none; color: var(--oc-muted-foreground); font-size: 0.75rem; }
40
+ .oc-doc-viewer__expand {
41
+ flex: none;
42
+ display: grid;
43
+ place-items: center;
44
+ inline-size: 2rem;
45
+ block-size: 2rem;
46
+ padding: 0;
47
+ border: 1px solid var(--oc-border);
48
+ border-radius: var(--oc-radius);
49
+ background: var(--oc-card);
50
+ color: var(--oc-foreground);
51
+ font-size: 0.95rem;
52
+ cursor: zoom-in;
53
+ transition: background var(--oc-transition-normal), border-color var(--oc-transition-normal);
54
+ }
55
+ .oc-doc-viewer__expand:hover { background: var(--oc-muted); border-color: var(--oc-border-strong, var(--oc-border)); }
56
+ .oc-doc-viewer__expand:focus-visible { outline: 2px solid var(--oc-ring); outline-offset: 2px; }
57
+
58
+ /* ---- Frame: A4 for PDF, 16/9 for video, free for images ---- */
59
+ .oc-doc-viewer__frame {
60
+ position: relative;
61
+ overflow: hidden;
62
+ aspect-ratio: 1 / 1.414;
63
+ border: 1px solid var(--oc-border);
64
+ border-radius: var(--oc-radius);
65
+ background: var(--oc-muted);
66
+ }
67
+ .oc-doc-viewer--video .oc-doc-viewer__frame { aspect-ratio: 16 / 9; background: var(--color-black-40); }
68
+ .oc-doc-viewer--image .oc-doc-viewer__frame { aspect-ratio: auto; }
69
+ .oc-doc-viewer__frame > :is(iframe, video, img, object, .oc-media-art) {
70
+ display: block;
71
+ inline-size: 100%;
72
+ block-size: 100%;
73
+ border: 0;
74
+ object-fit: contain;
75
+ }
76
+ .oc-doc-viewer--image .oc-doc-viewer__frame > img { block-size: auto; }
77
+
78
+ /* A whole-card link variant ("click for full screen") */
79
+ .oc-doc-viewer__frame > .oc-doc-viewer__open {
80
+ position: absolute;
81
+ inset: 0;
82
+ display: grid;
83
+ place-items: end center;
84
+ padding: 0.75rem;
85
+ border: 0;
86
+ background: linear-gradient(transparent 55%, oklch(from var(--color-black-50) l c h / 0.55));
87
+ color: var(--color-white-50);
88
+ font: inherit;
89
+ font-size: 0.8rem;
90
+ font-weight: 600;
91
+ opacity: 0;
92
+ cursor: zoom-in;
93
+ transition: opacity var(--oc-transition-normal);
94
+ }
95
+ .oc-doc-viewer__frame:hover > .oc-doc-viewer__open,
96
+ .oc-doc-viewer__open:focus-visible { opacity: 1; }
97
+ .oc-doc-viewer__open:focus-visible { outline: 2px solid var(--oc-ring); outline-offset: -4px; }
98
+
99
+ /* ---- Full-screen view ---- */
100
+ .oc-doc-viewer-dialog {
101
+ inline-size: min(100vw - 2rem, 72rem);
102
+ block-size: min(100dvh - 2rem, 60rem);
103
+ max-inline-size: none;
104
+ max-block-size: none;
105
+ padding: 0;
106
+ overflow: hidden;
107
+ border: 1px solid var(--oc-border);
108
+ border-radius: var(--oc-radius-lg, 12px);
109
+ background: var(--oc-card);
110
+ color: var(--oc-foreground);
111
+ }
112
+ .oc-doc-viewer-dialog[open] { display: grid; grid-template-rows: auto minmax(0, 1fr); }
113
+ .oc-doc-viewer-dialog::backdrop { background: oklch(from var(--color-black-60) l c h / 0.8); backdrop-filter: blur(6px); }
114
+ .oc-doc-viewer-dialog--video { background: var(--color-black-40); color: var(--color-white-50); }
115
+ .oc-doc-viewer-dialog__bar {
116
+ display: flex;
117
+ align-items: center;
118
+ justify-content: space-between;
119
+ gap: 1rem;
120
+ padding: 0.6rem 0.75rem 0.6rem 1rem;
121
+ border-block-end: 1px solid var(--oc-border);
122
+ }
123
+ .oc-doc-viewer-dialog--video .oc-doc-viewer-dialog__bar { border-color: oklch(from var(--color-white-50) l c h / 0.15); }
124
+ .oc-doc-viewer-dialog__title { margin: 0; overflow: hidden; font-size: 0.95rem; font-weight: 600; white-space: nowrap; text-overflow: ellipsis; }
125
+ .oc-doc-viewer-dialog__actions { display: flex; align-items: center; gap: 0.5rem; }
126
+ .oc-doc-viewer-dialog__close {
127
+ display: grid;
128
+ place-items: center;
129
+ inline-size: 2.25rem;
130
+ block-size: 2.25rem;
131
+ padding: 0;
132
+ border: 0;
133
+ border-radius: 50%;
134
+ background: transparent;
135
+ color: inherit;
136
+ font-size: 1.1rem;
137
+ cursor: pointer;
138
+ }
139
+ .oc-doc-viewer-dialog__close:hover { background: oklch(from currentColor l c h / 0.1); }
140
+ .oc-doc-viewer-dialog__close:focus-visible { outline: 2px solid var(--oc-ring); outline-offset: 2px; }
141
+ .oc-doc-viewer-dialog__body { display: grid; min-block-size: 0; }
142
+ .oc-doc-viewer-dialog__body > :is(iframe, video, img, object, .oc-media-art) {
143
+ inline-size: 100%;
144
+ block-size: 100%;
145
+ border: 0;
146
+ object-fit: contain;
147
+ }
148
+
149
+ @media (prefers-reduced-motion: reduce) {
150
+ .oc-doc-viewer__open, .oc-doc-viewer__expand { transition: none; }
151
+ }
@@ -0,0 +1,88 @@
1
+ /* File: packages/ui/src/parts/media-emoji-picker.css | Map: trigger button -> popover panel (anchor positioned)
2
+ -> group label -> grid -> emoji buttons -> inline modifier -> motion
3
+ Emoji picker: a popover with a grid of emoji buttons. Opening / closing is the popover attribute;
4
+ inserting the emoji into a field is one small optional script (see the snippet). */
5
+
6
+ /* Round trigger (a smiley), the same size as a toolbar icon button. */
7
+ .oc-emoji-picker__trigger {
8
+ display: inline-grid;
9
+ place-items: center;
10
+ inline-size: 2.25rem;
11
+ aspect-ratio: 1;
12
+ padding: 0;
13
+ border: 0;
14
+ border-radius: 50%;
15
+ background: transparent;
16
+ font-size: 1.15rem;
17
+ line-height: 1;
18
+ cursor: pointer;
19
+ }
20
+ .oc-emoji-picker__trigger:hover { background: var(--oc-surface-strong); }
21
+ .oc-emoji-picker__trigger:focus-visible { outline: 2px solid var(--oc-ring); outline-offset: 1px; }
22
+
23
+ /* The panel: floats above the trigger (implicit anchor of popovertarget), flips when there is no room. */
24
+ .oc-emoji-picker[popover] {
25
+ position: fixed;
26
+ inset: auto;
27
+ margin: 0 0 0.375rem;
28
+ position-area: block-start span-inline-end;
29
+ position-try-fallbacks: flip-block, flip-inline;
30
+ inline-size: 17.5rem;
31
+ max-block-size: 18rem;
32
+ overflow-y: auto;
33
+ padding: 0.5rem;
34
+ border: 1px solid var(--oc-border);
35
+ border-radius: calc(var(--oc-radius) + 2px);
36
+ background: var(--oc-popover);
37
+ color: var(--oc-popover-foreground);
38
+ box-shadow: 0 8px 24px oklch(from var(--color-black-60) l c h / 0.15);
39
+ scrollbar-width: thin;
40
+ }
41
+
42
+ /* Inline: the same panel, always visible inside the page (no popover). */
43
+ .oc-emoji-picker--inline {
44
+ inline-size: 17.5rem;
45
+ padding: 0.5rem;
46
+ border: 1px solid var(--oc-border);
47
+ border-radius: calc(var(--oc-radius) + 2px);
48
+ background: var(--oc-popover);
49
+ }
50
+
51
+ .oc-emoji-picker__label {
52
+ position: sticky;
53
+ inset-block-start: -0.5rem;
54
+ margin: 0;
55
+ padding: 0.375rem 0.25rem 0.25rem;
56
+ background: var(--oc-popover);
57
+ font-size: 0.6875rem;
58
+ font-weight: 600;
59
+ letter-spacing: 0.05em;
60
+ text-transform: uppercase;
61
+ color: var(--oc-muted-foreground);
62
+ }
63
+
64
+ .oc-emoji-picker__grid { display: grid; grid-template-columns: repeat(8, 1fr); gap: 0.125rem; margin-block-end: 0.25rem; }
65
+ .oc-emoji-picker__item {
66
+ display: grid;
67
+ place-items: center;
68
+ aspect-ratio: 1;
69
+ padding: 0;
70
+ border: 0;
71
+ border-radius: calc(var(--oc-radius) - 2px);
72
+ background: transparent;
73
+ font-size: 1.25rem;
74
+ line-height: 1;
75
+ cursor: pointer;
76
+ }
77
+ .oc-emoji-picker__item:hover { background: var(--oc-surface-strong); }
78
+ .oc-emoji-picker__item:focus-visible { outline: 2px solid var(--oc-ring); outline-offset: -2px; }
79
+
80
+ /* Fade + small rise when it opens. */
81
+ @media (prefers-reduced-motion: no-preference) {
82
+ .oc-emoji-picker[popover] {
83
+ transition: opacity var(--oc-transition-fast), translate var(--oc-transition-fast), display var(--oc-transition-fast) allow-discrete, overlay var(--oc-transition-fast) allow-discrete;
84
+ }
85
+ .oc-emoji-picker[popover]:popover-open { @starting-style { opacity: 0; translate: 0 0.25rem; } }
86
+ .oc-emoji-picker__item { transition: transform var(--oc-transition-fast); }
87
+ .oc-emoji-picker__item:hover { transform: scale(1.15); }
88
+ }
@@ -0,0 +1,52 @@
1
+ /* File: packages/ui/src/parts/media-entity-card.css | Map: card grid (frame | body) -> framed media cell
2
+ -> glyph -> body (eyebrow, title, description) -> facts (key / value) -> stacked + small modifiers -> narrow containers
3
+ Entity card: a framed square "specimen" cell (image, glyph, icon) next to a detail column.
4
+ Good for dictionary entries, collection items, symbols, products. */
5
+
6
+ .oc-entity-card {
7
+ container-type: inline-size;
8
+ inline-size: 100%;
9
+ border: 1px solid var(--oc-border);
10
+ border-radius: calc(var(--oc-radius) + 4px);
11
+ background: var(--oc-card);
12
+ color: var(--oc-foreground);
13
+ }
14
+ .oc-entity-card__grid { display: grid; grid-template-columns: 9rem minmax(0, 1fr); gap: 1.25rem; padding: 1.25rem; }
15
+
16
+ /* Framed cell: a double frame (border + inset ring) around a soft radial background. */
17
+ .oc-entity-card__frame {
18
+ display: grid;
19
+ place-items: center;
20
+ aspect-ratio: 1;
21
+ overflow: hidden;
22
+ border: 1px solid var(--oc-border-strong);
23
+ border-radius: var(--oc-radius);
24
+ background: radial-gradient(circle at 50% 40%, var(--oc-surface-strong), var(--oc-muted));
25
+ box-shadow: inset 0 0 0 0.35rem var(--oc-card), inset 0 0 0 calc(0.35rem + 1px) var(--oc-border);
26
+ }
27
+ .oc-entity-card__frame > :is(img, svg, .oc-media-art) { inline-size: 100%; block-size: 100%; object-fit: cover; }
28
+ /* Contained pictures (logos, symbols) keep a margin inside the frame. */
29
+ .oc-entity-card__frame--contain > :is(img, svg) { inline-size: 70%; block-size: 70%; object-fit: contain; }
30
+ .oc-entity-card__glyph { font-size: 3.5rem; line-height: 1; color: var(--oc-foreground); }
31
+
32
+ /* Detail column. */
33
+ .oc-entity-card__body { display: grid; align-content: start; gap: 0.35rem; min-inline-size: 0; }
34
+ .oc-entity-card__eyebrow { margin: 0; font-size: 0.75rem; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; color: var(--oc-primary); }
35
+ .oc-entity-card__title { margin: 0; font-size: 1.15rem; font-weight: 700; line-height: 1.3; }
36
+ .oc-entity-card__description { margin: 0; font-size: 0.875rem; color: var(--oc-muted-foreground); }
37
+
38
+ /* Facts: small key / value grid. */
39
+ .oc-entity-card__facts { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 0.25rem 1rem; margin: 0.5rem 0 0; font-size: 0.8125rem; }
40
+ .oc-entity-card__facts > dt { color: var(--oc-muted-foreground); }
41
+ .oc-entity-card__facts > dd { margin: 0; font-weight: 500; }
42
+
43
+ /* Footer row for tags or actions. */
44
+ .oc-entity-card__footer { display: flex; flex-wrap: wrap; gap: 0.5rem; padding: 0.75rem 1.25rem; border-block-start: 1px solid var(--oc-border); }
45
+
46
+ /* Stacked: frame on top (also used automatically in narrow containers). */
47
+ .oc-entity-card--stacked .oc-entity-card__grid { grid-template-columns: minmax(0, 1fr); }
48
+ .oc-entity-card--sm .oc-entity-card__grid { grid-template-columns: 5.5rem minmax(0, 1fr); gap: 0.875rem; padding: 0.875rem; }
49
+ .oc-entity-card--sm .oc-entity-card__glyph { font-size: 2.25rem; }
50
+ @container (max-width: 22rem) {
51
+ .oc-entity-card__grid { grid-template-columns: minmax(0, 1fr); }
52
+ }
@@ -0,0 +1,78 @@
1
+ /* File: packages/ui/src/parts/media-flip-card.css | Map: card (perspective) -> hidden checkbox -> inner (3D) -> faces
2
+ -> flip label -> flipped states (checkbox, .is-flipped, --hover) -> vertical -> reduced motion (cross-fade)
3
+ Flip card: front and back in one grid cell; the inner layer turns 180deg. Triggers: a checkbox (CSS :has()),
4
+ hover / focus (--hover) or the .is-flipped class for frameworks. No JavaScript. */
5
+
6
+ .oc-flip-card {
7
+ --oc-flip-card-turn: rotateY(180deg);
8
+ position: relative;
9
+ display: block;
10
+ inline-size: 100%;
11
+ perspective: 60rem;
12
+ }
13
+
14
+ /* The checkbox stays in the page (focusable, Space toggles) but is invisible. */
15
+ .oc-flip-card__toggle { position: absolute; inline-size: 1px; block-size: 1px; margin: 0; opacity: 0; pointer-events: none; }
16
+
17
+ .oc-flip-card__inner {
18
+ display: grid;
19
+ transform-style: preserve-3d;
20
+ transition: transform 0.6s cubic-bezier(0.4, 0.2, 0.2, 1);
21
+ }
22
+
23
+ /* Both faces share one grid cell, so the card is as tall as the taller face. */
24
+ .oc-flip-card__face {
25
+ position: relative;
26
+ grid-area: 1 / 1;
27
+ display: grid;
28
+ align-content: center;
29
+ gap: 0.5rem;
30
+ min-block-size: 10rem;
31
+ padding: 1.25rem;
32
+ border: 1px solid var(--oc-border);
33
+ border-radius: calc(var(--oc-radius) + 4px);
34
+ background: var(--oc-card);
35
+ color: var(--oc-foreground);
36
+ backface-visibility: hidden;
37
+ }
38
+ .oc-flip-card__face--back {
39
+ transform: var(--oc-flip-card-turn);
40
+ border-color: transparent;
41
+ background: var(--oc-primary);
42
+ color: var(--oc-primary-foreground);
43
+ }
44
+ .oc-flip-card__title { margin: 0; font-size: 1.25rem; font-weight: 700; }
45
+ .oc-flip-card__text { margin: 0; font-size: 0.875rem; opacity: 0.85; }
46
+
47
+ /* Flip label ("Show answer"): its ::after covers the face, so a click anywhere flips. */
48
+ .oc-flip-card__flip { justify-self: start; font-size: 0.8125rem; font-weight: 600; text-decoration: underline; text-underline-offset: 3px; cursor: pointer; }
49
+ .oc-flip-card__flip::after { content: ""; position: absolute; inset: 0; border-radius: inherit; }
50
+
51
+ /* Keyboard focus on the checkbox (or inside a --hover card) rings the card. */
52
+ .oc-flip-card:has(.oc-flip-card__toggle:focus-visible) .oc-flip-card__face,
53
+ .oc-flip-card--hover:focus-visible .oc-flip-card__face { outline: 2px solid var(--oc-ring); outline-offset: 2px; }
54
+ .oc-flip-card--hover:focus-visible { outline: none; }
55
+
56
+ /* Flipped: checkbox checked, .is-flipped, or hover / focus on a --hover card. */
57
+ .oc-flip-card:has(> .oc-flip-card__toggle:checked) .oc-flip-card__inner,
58
+ .oc-flip-card.is-flipped .oc-flip-card__inner,
59
+ .oc-flip-card--hover:is(:hover, :focus-within) .oc-flip-card__inner { transform: var(--oc-flip-card-turn); }
60
+
61
+ /* Vertical flip (top over bottom). */
62
+ .oc-flip-card--vertical { --oc-flip-card-turn: rotateX(180deg); }
63
+
64
+ /* Reduced motion: no turning. The faces simply cross-fade. */
65
+ @media (prefers-reduced-motion: reduce) {
66
+ .oc-flip-card .oc-flip-card__inner { transform: none; transition: none; }
67
+ .oc-flip-card .oc-flip-card__face { transform: none; transition: opacity 0.2s, visibility 0.2s; }
68
+ .oc-flip-card .oc-flip-card__face--back { opacity: 0; visibility: hidden; }
69
+ .oc-flip-card:has(> .oc-flip-card__toggle:checked) .oc-flip-card__inner,
70
+ .oc-flip-card.is-flipped .oc-flip-card__inner,
71
+ .oc-flip-card--hover:is(:hover, :focus-within) .oc-flip-card__inner { transform: none; }
72
+ .oc-flip-card:has(> .oc-flip-card__toggle:checked) .oc-flip-card__face--front,
73
+ .oc-flip-card.is-flipped .oc-flip-card__face--front,
74
+ .oc-flip-card--hover:is(:hover, :focus-within) .oc-flip-card__face--front { opacity: 0; visibility: hidden; }
75
+ .oc-flip-card:has(> .oc-flip-card__toggle:checked) .oc-flip-card__face--back,
76
+ .oc-flip-card.is-flipped .oc-flip-card__face--back,
77
+ .oc-flip-card--hover:is(:hover, :focus-within) .oc-flip-card__face--back { opacity: 1; visibility: visible; }
78
+ }
@@ -0,0 +1,68 @@
1
+ /* File: packages/ui/src/parts/media-image-zoom.css | Map: root + zoom variable -> toolbar + level chips
2
+ -> viewport (scroll = pan) -> canvas (grows with the zoom) -> zoom levels (classes + radios via :has()) -> motion
3
+ Image zoom: the canvas gets wider than the viewport, so the native scrollbars / touch scrolling pan the image.
4
+ No JavaScript and no transform tricks: zooming is just inline-size: calc(100% * var(--oc-zoom)). */
5
+
6
+ .oc-image-zoom {
7
+ --oc-zoom: 1;
8
+ --oc-image-zoom-ratio: 16 / 9;
9
+ display: grid;
10
+ gap: 0.5rem;
11
+ inline-size: 100%;
12
+ }
13
+
14
+ /* ---- Toolbar with zoom level chips (radio buttons) ---- */
15
+ .oc-image-zoom__toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 0.25rem; font-size: 0.8125rem; color: var(--oc-muted-foreground); }
16
+ .oc-image-zoom__label { margin-inline-end: 0.25rem; }
17
+ .oc-image-zoom__level {
18
+ position: relative;
19
+ display: inline-grid;
20
+ place-items: center;
21
+ min-inline-size: 2.5rem;
22
+ padding: 0.25rem 0.5rem;
23
+ border: 1px solid var(--oc-border);
24
+ border-radius: 999px;
25
+ background: var(--oc-card);
26
+ color: var(--oc-foreground);
27
+ font-variant-numeric: tabular-nums;
28
+ cursor: pointer;
29
+ }
30
+ /* The radio itself is invisible but stays focusable (keyboard: arrow keys switch levels). */
31
+ .oc-image-zoom__level > input { position: absolute; inset: 0; margin: 0; opacity: 0; cursor: pointer; }
32
+ .oc-image-zoom__level:has(input:checked) { border-color: var(--oc-primary); background: var(--oc-primary); color: var(--oc-primary-foreground); }
33
+ .oc-image-zoom__level:has(input:focus-visible) { outline: 2px solid var(--oc-ring); outline-offset: 2px; }
34
+
35
+ /* ---- Viewport: fixed ratio window; scrolling it pans the image ---- */
36
+ .oc-image-zoom__viewport {
37
+ aspect-ratio: var(--oc-image-zoom-ratio);
38
+ overflow: auto;
39
+ overscroll-behavior: contain;
40
+ border: 1px solid var(--oc-border);
41
+ border-radius: var(--oc-radius);
42
+ background: var(--oc-muted);
43
+ scrollbar-width: thin;
44
+ }
45
+ .oc-image-zoom__viewport:focus-visible { outline: 2px solid var(--oc-ring); outline-offset: 2px; }
46
+
47
+ /* ---- Canvas: same ratio as the viewport, scaled by --oc-zoom ---- */
48
+ .oc-image-zoom__canvas { inline-size: calc(100% * var(--oc-zoom)); aspect-ratio: var(--oc-image-zoom-ratio); }
49
+ .oc-image-zoom__canvas > * { display: block; inline-size: 100%; block-size: 100%; object-fit: cover; }
50
+
51
+ /* ---- Zoom levels: fixed classes, or radio chips (value = level) ---- */
52
+ .oc-image-zoom--150 { --oc-zoom: 1.5; }
53
+ .oc-image-zoom--200 { --oc-zoom: 2; }
54
+ .oc-image-zoom--300 { --oc-zoom: 3; }
55
+ .oc-image-zoom:has(.oc-image-zoom__level input[value="1"]:checked) { --oc-zoom: 1; }
56
+ .oc-image-zoom:has(.oc-image-zoom__level input[value="1.5"]:checked) { --oc-zoom: 1.5; }
57
+ .oc-image-zoom:has(.oc-image-zoom__level input[value="2"]:checked) { --oc-zoom: 2; }
58
+ .oc-image-zoom:has(.oc-image-zoom__level input[value="3"]:checked) { --oc-zoom: 3; }
59
+ .oc-image-zoom:has(.oc-image-zoom__level input[value="4"]:checked) { --oc-zoom: 4; }
60
+
61
+ /* Ratio presets. */
62
+ .oc-image-zoom--square { --oc-image-zoom-ratio: 1 / 1; }
63
+ .oc-image-zoom--photo { --oc-image-zoom-ratio: 4 / 3; }
64
+
65
+ /* Smooth resize only when motion is welcome. */
66
+ @media (prefers-reduced-motion: no-preference) {
67
+ .oc-image-zoom__canvas { transition: inline-size var(--oc-transition-normal); }
68
+ }
@@ -0,0 +1,131 @@
1
+ /* File: packages/ui/src/parts/media-lightbox.css | Map: thumbnails -> dialog + backdrop -> bar -> track + slides
2
+ -> nav buttons -> caption + counter -> single image -> small screens -> motion
3
+ Lightbox: one native <dialog> with a scroll-snap track of slides. Thumbnails open it with
4
+ command="show-modal"; prev / next are anchor links to the slide ids, so it works without JavaScript. */
5
+
6
+ /* ---- Thumbnails (buttons that open the dialog) ---- */
7
+ .oc-lightbox-thumbs { display: grid; grid-template-columns: repeat(auto-fill, minmax(8rem, 1fr)); gap: 0.5rem; inline-size: 100%; }
8
+ .oc-lightbox-thumb {
9
+ display: block;
10
+ padding: 0;
11
+ aspect-ratio: 4 / 3;
12
+ overflow: hidden;
13
+ border: 1px solid var(--oc-border);
14
+ border-radius: var(--oc-radius);
15
+ background: var(--oc-muted);
16
+ cursor: zoom-in;
17
+ }
18
+ .oc-lightbox-thumb > * { inline-size: 100%; block-size: 100%; object-fit: cover; transition: transform var(--oc-transition-normal); }
19
+ .oc-lightbox-thumb:hover > * { transform: scale(1.04); }
20
+ .oc-lightbox-thumb:focus-visible { outline: 2px solid var(--oc-ring); outline-offset: 2px; }
21
+
22
+ /* ---- The viewer: full-screen modal dialog, dark and blurred behind ---- */
23
+ .oc-lightbox {
24
+ inline-size: 100vw;
25
+ block-size: 100dvh;
26
+ max-inline-size: none;
27
+ max-block-size: none;
28
+ margin: 0;
29
+ padding: 0;
30
+ border: 0;
31
+ overflow: hidden;
32
+ background: transparent;
33
+ color: var(--color-white-50);
34
+ }
35
+ .oc-lightbox[open] { display: grid; grid-template-rows: auto minmax(0, 1fr); }
36
+ .oc-lightbox::backdrop { background: oklch(from var(--color-black-60) l c h / 0.88); backdrop-filter: blur(10px); }
37
+
38
+ /* ---- Top bar: title left, close button right ---- */
39
+ .oc-lightbox__bar { display: flex; align-items: center; justify-content: space-between; gap: 1rem; padding: 0.75rem 1rem; }
40
+ .oc-lightbox__title { margin: 0; font-size: 0.95rem; font-weight: 600; }
41
+
42
+ /* ---- Track: one slide per screen, snapping. Arrow keys scroll it when it has focus. ---- */
43
+ .oc-lightbox__track {
44
+ display: flex;
45
+ min-block-size: 0;
46
+ overflow-x: auto;
47
+ overscroll-behavior: contain;
48
+ scroll-snap-type: x mandatory;
49
+ scrollbar-width: none;
50
+ }
51
+ .oc-lightbox__track:focus-visible { outline: 2px solid var(--oc-ring); outline-offset: -2px; }
52
+ .oc-lightbox__slide {
53
+ flex: 0 0 100%;
54
+ display: grid;
55
+ grid-template-columns: 4.5rem minmax(0, 1fr) 4.5rem;
56
+ grid-template-rows: minmax(0, 1fr) auto;
57
+ align-items: center;
58
+ gap: 1rem 0;
59
+ margin: 0;
60
+ padding-block-end: 1.5rem;
61
+ scroll-snap-align: center;
62
+ scroll-snap-stop: always;
63
+ }
64
+ .oc-lightbox__media { grid-column: 2; grid-row: 1; display: grid; place-items: center; min-block-size: 0; block-size: 100%; }
65
+ .oc-lightbox__media > * {
66
+ max-inline-size: 100%;
67
+ max-block-size: calc(100dvh - 9rem);
68
+ object-fit: contain;
69
+ border: 1px solid oklch(from var(--color-white-50) l c h / 0.15);
70
+ border-radius: calc(var(--oc-radius) - 2px);
71
+ box-shadow: 0 40px 100px -30px oklch(from var(--color-black-60) l c h / 0.9);
72
+ }
73
+ /* Placeholder artwork has no natural size: give it one. */
74
+ .oc-lightbox__media > .oc-media-art { inline-size: min(56rem, 100%); block-size: auto; aspect-ratio: 3 / 2; }
75
+
76
+ /* ---- Round buttons: close (button) and prev / next (links) ---- */
77
+ .oc-lightbox__close,
78
+ .oc-lightbox__nav {
79
+ display: inline-grid;
80
+ place-items: center;
81
+ inline-size: 2.75rem;
82
+ aspect-ratio: 1;
83
+ border: 1px solid oklch(from var(--color-white-50) l c h / 0.25);
84
+ border-radius: 50%;
85
+ background: oklch(from var(--color-white-50) l c h / 0.08);
86
+ color: inherit;
87
+ font-size: 1.1rem;
88
+ line-height: 1;
89
+ text-decoration: none;
90
+ cursor: pointer;
91
+ transition: background var(--oc-transition-fast), transform var(--oc-transition-fast);
92
+ }
93
+ .oc-lightbox__close:hover,
94
+ .oc-lightbox__nav:hover { background: oklch(from var(--color-white-50) l c h / 0.18); transform: scale(1.06); }
95
+ .oc-lightbox__close:focus-visible,
96
+ .oc-lightbox__nav:focus-visible { outline: 2px solid var(--oc-ring); outline-offset: 2px; }
97
+ .oc-lightbox__nav { grid-row: 1; justify-self: center; }
98
+ .oc-lightbox__nav--prev { grid-column: 1; }
99
+ .oc-lightbox__nav--next { grid-column: 3; }
100
+
101
+ /* ---- Caption with the "2 / 5" counter ---- */
102
+ .oc-lightbox__caption {
103
+ grid-column: 1 / -1;
104
+ grid-row: 2;
105
+ display: flex;
106
+ flex-wrap: wrap;
107
+ justify-content: center;
108
+ gap: 0.25rem 0.75rem;
109
+ padding-inline: 1rem;
110
+ font-size: 0.95rem;
111
+ text-align: center;
112
+ color: oklch(from var(--color-white-50) l c h / 0.8);
113
+ }
114
+ .oc-lightbox__counter { font-family: ui-monospace, monospace; font-variant-numeric: tabular-nums; color: oklch(from var(--color-white-50) l c h / 0.6); }
115
+
116
+ /* One image only: no prev / next and no counter needed. */
117
+ .oc-lightbox--single .oc-lightbox__nav,
118
+ .oc-lightbox--single .oc-lightbox__counter { visibility: hidden; }
119
+
120
+ /* Small screens: narrower side columns, smaller buttons. */
121
+ @media (max-width: 40rem) {
122
+ .oc-lightbox__slide { grid-template-columns: 2.75rem minmax(0, 1fr) 2.75rem; }
123
+ .oc-lightbox__nav { inline-size: 2.25rem; }
124
+ }
125
+
126
+ /* Motion only when the user has not asked for less. */
127
+ @media (prefers-reduced-motion: no-preference) {
128
+ .oc-lightbox__track { scroll-behavior: smooth; }
129
+ .oc-lightbox[open] { animation: oc-lightbox-in 0.25s ease-out; }
130
+ }
131
+ @keyframes oc-lightbox-in { from { opacity: 0; scale: 0.98; } }