@birdycom/omniaui 0.12.4 → 0.12.6

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 (79) hide show
  1. package/README.md +1 -1
  2. package/bin/lib/copy-js.mjs +10 -1
  3. package/bin/omniaui.mjs +2 -2
  4. package/dist/barcode.d.ts +15 -0
  5. package/dist/barcode.js +164 -0
  6. package/dist/components-extra.css +15 -1
  7. package/dist/components.css +1496 -193
  8. package/dist/components.min.css +1 -1
  9. package/dist/data-table.js +135 -98
  10. package/dist/form-extras.d.ts +11 -0
  11. package/dist/form-extras.js +214 -0
  12. package/dist/lazy.d.ts +5 -0
  13. package/dist/lazy.js +37 -0
  14. package/dist/oc-theme.css +1 -1
  15. package/dist/parts/app-data-table-footer.css +38 -0
  16. package/dist/parts/app-data-table-rich.css +161 -0
  17. package/dist/parts/app-data-table.css +44 -74
  18. package/dist/parts/app-name-card.css +65 -0
  19. package/dist/parts/app-titlebar.css +16 -8
  20. package/dist/parts/data-meter-row.css +40 -0
  21. package/dist/parts/data-price.css +20 -0
  22. package/dist/parts/data-ribbon.css +76 -0
  23. package/dist/parts/data-spec-list.css +36 -0
  24. package/dist/parts/data-stock.css +41 -0
  25. package/dist/parts/forms-affix-input.css +1 -1
  26. package/dist/parts/forms-avatar-picker.css +43 -0
  27. package/dist/parts/forms-date-field.css +53 -0
  28. package/dist/parts/forms-density.css +13 -0
  29. package/dist/parts/forms-form-section.css +64 -0
  30. package/dist/parts/forms-quantity.css +55 -0
  31. package/dist/parts/forms-rating.css +81 -0
  32. package/dist/parts/media-attachment.css +1 -1
  33. package/dist/parts/media-barcode.css +23 -0
  34. package/dist/parts/media-chat-composer.css +3 -0
  35. package/dist/parts/media-emoji-picker.css +3 -1
  36. package/dist/parts/media-gallery.css +55 -0
  37. package/dist/parts/media-glass-liquid.css +99 -0
  38. package/dist/parts/media-glass-options.css +50 -0
  39. package/dist/parts/media-lazy.css +46 -0
  40. package/dist/parts/media-message-thread.css +1 -1
  41. package/dist/parts/media-message.css +1 -1
  42. package/dist/parts/nav-topbar-variants.css +96 -0
  43. package/dist/parts/site-header-variants.css +70 -0
  44. package/dist/parts/site-prose.css +1 -1
  45. package/dist/showcase.css +40 -9
  46. package/manifest.json +121 -9
  47. package/package.json +5 -2
  48. package/snippets/accordion-accent.html +1 -1
  49. package/snippets/attachment.html +4 -4
  50. package/snippets/avatar-picker.html +40 -0
  51. package/snippets/barcode.html +20 -0
  52. package/snippets/chat-composer.html +3 -3
  53. package/snippets/data-table.html +51 -76
  54. package/snippets/date-field.html +44 -0
  55. package/snippets/doc-viewer.html +1 -1
  56. package/snippets/emoji-picker.html +1 -1
  57. package/snippets/form-density.html +25 -0
  58. package/snippets/form-section.html +47 -0
  59. package/snippets/gallery.html +25 -0
  60. package/snippets/lazy.html +39 -0
  61. package/snippets/message-thread.html +12 -12
  62. package/snippets/message.html +10 -10
  63. package/snippets/meter-row.html +29 -0
  64. package/snippets/name-card.html +72 -0
  65. package/snippets/page-header.html +1 -1
  66. package/snippets/price.html +25 -0
  67. package/snippets/prose.html +3 -3
  68. package/snippets/quantity.html +12 -0
  69. package/snippets/rating.html +26 -0
  70. package/snippets/ribbon.html +48 -0
  71. package/snippets/sidebar.html +3 -3
  72. package/snippets/spec-list.html +34 -0
  73. package/snippets/stock.html +20 -0
  74. package/snippets/table-actions.html +5 -5
  75. package/snippets/table.html +2 -2
  76. package/snippets/titlebar.html +1 -1
  77. package/snippets/user.html +3 -3
  78. package/dist/parts/app-column-menu.css +0 -85
  79. package/snippets/column-menu.html +0 -38
@@ -0,0 +1,46 @@
1
+ /* File: packages/ui/src/parts/media-lazy.css | Map: box (aspect ratio, shimmer placeholder or a tiny blurred preview) -> image fades in -> error -> lazy sections
2
+ Lazy loading. .oc-lazy: an image box that keeps its place, shows a shimmer (or --oc-lazy-preview, a tiny blurred
3
+ copy) until the photo is there and then fades it in (lazy.js adds .is-loaded). .oc-lazy-section: content the browser
4
+ only lays out and draws when it comes near the screen (content-visibility). */
5
+
6
+ .oc-lazy {
7
+ --oc-lazy-ratio: 16 / 10;
8
+ position: relative;
9
+ display: block;
10
+ margin: 0;
11
+ overflow: hidden;
12
+ aspect-ratio: var(--oc-lazy-ratio);
13
+ border-radius: var(--oc-radius);
14
+ background:
15
+ linear-gradient(100deg, transparent 30%, oklch(from var(--oc-background) l c h / 0.55) 50%, transparent 70%) -150% 0 / 60% 100% no-repeat,
16
+ var(--oc-muted);
17
+ animation: oc-lazy-shimmer 1.4s ease-in-out infinite;
18
+ }
19
+ /* A tiny blurred preview instead of the shimmer: style="--oc-lazy-preview: url(photo-20px.webp)". */
20
+ .oc-lazy[style*="--oc-lazy-preview"] { animation: none; background: var(--oc-muted); }
21
+ .oc-lazy[style*="--oc-lazy-preview"]::before { content: ""; position: absolute; inset: 0; background: var(--oc-lazy-preview) center / cover no-repeat; filter: blur(14px); scale: 1.15; }
22
+ .oc-lazy > img { position: relative; display: block; inline-size: 100%; block-size: 100%; object-fit: cover; opacity: 0; transition: opacity 0.45s ease; }
23
+ .oc-lazy.is-loaded { animation: none; background: none; }
24
+ .oc-lazy.is-loaded::before { display: none; }
25
+ .oc-lazy.is-loaded > img { opacity: 1; }
26
+ .oc-lazy.is-error { animation: none; }
27
+ .oc-lazy.is-error > img { opacity: 0; }
28
+ .oc-lazy > figcaption { position: absolute; inset: auto 0 0; padding: 0.4rem 0.6rem; font-size: 0.75rem; color: var(--color-white-50); background: linear-gradient(transparent, oklch(0% 0 0deg / 0.55)); }
29
+ /* Without the script (or when it is off) the photo shows as soon as it is there. */
30
+ @media (scripting: none) { .oc-lazy > img { opacity: 1; } }
31
+ @keyframes oc-lazy-shimmer { to { background-position: 250% 0, 0 0; } }
32
+
33
+ /* Sections far down a long page: laid out and drawn only when they come near the screen. */
34
+ .oc-lazy-section { content-visibility: auto; contain-intrinsic-size: auto var(--oc-lazy-size, 600px); }
35
+
36
+ @media (prefers-reduced-motion: reduce) {
37
+ .oc-lazy { animation: none; }
38
+ .oc-lazy > img { transition: none; }
39
+ }
40
+
41
+ /* In a table or a list: a small square thumbnail, and placeholder rows (.oc-lazy-row) while the next page loads. */
42
+ .oc-lazy.oc-lazy--thumb { --oc-lazy-ratio: 1; inline-size: 2.5rem; flex: none; border-radius: calc(var(--oc-radius) - 2px); }
43
+ .oc-lazy-row > :is(td, th) { padding-block: 0.6rem; }
44
+ .oc-lazy-row .oc-lazy-bar { display: block; block-size: 0.75rem; inline-size: var(--oc-lazy-bar, 70%); border-radius: 999px; background: var(--oc-muted); animation: oc-lazy-pulse 1.2s ease-in-out infinite alternate; }
45
+ @keyframes oc-lazy-pulse { to { opacity: 0.45; } }
46
+ @media (prefers-reduced-motion: reduce) { .oc-lazy-row .oc-lazy-bar { animation: none; } }
@@ -2,7 +2,7 @@
2
2
  people, status, actions) -> wallpaper (masked doodles in a token color) -> scroll area (starts at the bottom)
3
3
  -> composer footer -> sending overlay (aria-busy) -> reduced motion
4
4
  Message thread: one conversation. The scroll area uses flex-direction: column-reverse, so it opens at the newest message
5
- and stays there when messages are added, without a script. Based on the question chat of the Korrekt app. */
5
+ and stays there when messages are added, without a script. */
6
6
 
7
7
  .oc-message-thread {
8
8
  --oc-message-thread-height: 32rem;
@@ -2,7 +2,7 @@
2
2
  -> grouping (:has) -> hover actions -> date divider + system line -> reduced motion
3
3
  Message: one chat bubble. Incoming on the left with the avatar, outgoing on the right in the primary tint.
4
4
  Consecutive messages of one sender group by themselves: the avatar shows once (on the last one), the name once (on the
5
- first one). Based on the question chat of the Korrekt app. */
5
+ first one). */
6
6
 
7
7
  .oc-message-list {
8
8
  --oc-message-in: var(--oc-card);
@@ -0,0 +1,96 @@
1
+ /* File: packages/ui/src/parts/nav-topbar-variants.css
2
+ Map: brand (logo tile, room before the menu) -> centred menu -> plain links -> dark / brand colour -> floating pill
3
+ -> strip above the bar -> footer bar (split, links, dark)
4
+ Variants of the site header (.oc-topbar in docs-ui.css) and the footer bar (.oc-footerbar). Colours are tokens. */
5
+
6
+ /* Brand: a logo tile and the name, always with room before the menu. */
7
+ .oc-topbar-brand { display: inline-flex; align-items: center; gap: 0.55rem; margin-inline-end: 1rem; }
8
+ .oc-topbar-logo {
9
+ display: grid;
10
+ place-items: center;
11
+ flex: none;
12
+ inline-size: 1.85rem;
13
+ block-size: 1.85rem;
14
+ border-radius: 9px;
15
+ color: var(--color-white-50);
16
+ background: linear-gradient(135deg, var(--oc-accent-from, var(--oc-primary)), var(--oc-accent-to, var(--oc-primary)));
17
+ font-size: 0.95rem;
18
+ }
19
+ .oc-topbar-logo > :is(svg, iconify-icon) { inline-size: 1.1rem; block-size: 1.1rem; font-size: 1.1rem; }
20
+ .oc-topbar-nav { min-inline-size: 0; }
21
+ .oc-topbar-actions .oc-btn { white-space: nowrap; }
22
+
23
+ /* Centred menu: brand left, menu in the middle, actions right (from 761px; smaller screens keep the burger). */
24
+ @media (min-width: 761px) {
25
+ .oc-topbar--center { display: grid; grid-template-columns: 1fr auto 1fr; }
26
+ .oc-topbar--center .oc-topbar-brand { justify-self: start; }
27
+ .oc-topbar--center .oc-topbar-actions { justify-self: end; }
28
+ }
29
+
30
+ /* Plain links: no pill frames, the current page underlined. */
31
+ .oc-topbar--plain .oc-navmenu--site .oc-navmenu-link {
32
+ border-color: transparent;
33
+ border-radius: 0;
34
+ background: none;
35
+ color: var(--oc-muted-foreground);
36
+ box-shadow: inset 0 -2px 0 transparent;
37
+ }
38
+ .oc-topbar--plain .oc-navmenu--site .oc-navmenu-link:hover { color: var(--oc-foreground); border-color: transparent; background: none; }
39
+ .oc-topbar--plain .oc-navmenu--site .oc-navmenu-link[aria-current="page"] {
40
+ color: var(--oc-foreground);
41
+ border-color: transparent;
42
+ background: none;
43
+ box-shadow: inset 0 -2px 0 var(--oc-primary);
44
+ }
45
+
46
+ /* Dark bar and brand-colour bar: light text, links as soft pills. */
47
+ .oc-topbar:is(.oc-topbar--dark, .oc-topbar--brand) { color: var(--color-white-50); border-block-end-color: transparent; }
48
+ .oc-topbar.oc-topbar--dark { background: var(--oc-dark-band, var(--color-duskblue-95)); }
49
+ .oc-topbar--brand { background: linear-gradient(100deg, var(--oc-accent-from, var(--oc-primary)), var(--oc-accent-to, var(--oc-primary))); }
50
+ .oc-topbar:is(.oc-topbar--dark, .oc-topbar--brand) .oc-navmenu--site .oc-navmenu-link {
51
+ color: oklch(from var(--color-white-50) l c h / 0.8);
52
+ border-color: transparent;
53
+ background: none;
54
+ }
55
+ .oc-topbar:is(.oc-topbar--dark, .oc-topbar--brand) .oc-navmenu--site .oc-navmenu-link:is(:hover, [aria-current="page"]) {
56
+ color: var(--color-white-50);
57
+ border-color: transparent;
58
+ background: oklch(from var(--color-white-50) l c h / 0.14);
59
+ }
60
+ .oc-topbar--dark .oc-topbar-logo { background: oklch(from var(--color-white-50) l c h / 0.14); }
61
+ .oc-topbar--brand .oc-topbar-logo { color: var(--oc-primary); background: var(--color-white-50); }
62
+ .oc-topbar:is(.oc-topbar--dark, .oc-topbar--brand) .oc-topbar-burger { color: inherit; border-color: oklch(from var(--color-white-50) l c h / 0.3); }
63
+
64
+ /* Floating: a rounded bar with room around it and a soft shadow (over a hero or a photo). */
65
+ .oc-topbar--floating {
66
+ margin: 0.75rem;
67
+ border: 1px solid var(--oc-border);
68
+ border-radius: 16px;
69
+ box-shadow: 0 14px 34px -18px oklch(from var(--color-black-60) l c h / 0.35);
70
+ }
71
+
72
+ /* Strip: a small row above the bar (contact, language, account). */
73
+ .oc-topbar-strip {
74
+ display: flex;
75
+ flex-wrap: wrap;
76
+ align-items: center;
77
+ justify-content: space-between;
78
+ gap: 0.25rem 1rem;
79
+ padding: 0.35rem clamp(14px, 2vw, 28px);
80
+ font-size: 0.78rem;
81
+ color: var(--oc-muted-foreground);
82
+ background: var(--oc-muted);
83
+ }
84
+ .oc-topbar-strip a { color: inherit; text-decoration: none; }
85
+ .oc-topbar-strip a:hover { color: var(--oc-foreground); }
86
+ .oc-topbar-strip > span { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 0.25rem 1rem; }
87
+
88
+ /* Footer bar: text left and links right (--split), a link list, a dark bar. */
89
+ .oc-footerbar--split { justify-content: space-between; gap: 1rem; text-align: start; }
90
+ .oc-footerbar--tall { block-size: auto; min-block-size: var(--ocm-footer-height, 2.25rem); padding-block: 0.85rem; flex-wrap: wrap; }
91
+ .oc-footerbar-links { display: inline-flex; flex-wrap: wrap; gap: 0.25rem 1rem; margin: 0; padding: 0; list-style: none; }
92
+ .oc-footerbar-links a { color: var(--oc-muted-foreground); text-decoration: none; }
93
+ .oc-footerbar-links a:hover { color: var(--oc-foreground); }
94
+ .oc-footerbar.oc-footerbar--dark { color: oklch(from var(--color-white-50) l c h / 0.7); background: var(--oc-dark-band, var(--color-duskblue-95)); border-block-start-color: transparent; }
95
+ .oc-footerbar--dark :is(a, .oc-footerbar-links a) { color: oklch(from var(--color-white-50) l c h / 0.85); }
96
+ .oc-footerbar--dark .oc-footerbar-links a:hover { color: var(--color-white-50); }
@@ -0,0 +1,70 @@
1
+ /* File: packages/ui/src/parts/site-header-variants.css
2
+ Map: actions slot -> centred menu -> dark -> floating pill -> glass pill -> footer: centred, dark, newsletter, social
3
+ Variants of the marketing header (.oc-site-header) and footer (.oc-site-footer) in showcase.css. The sticky header
4
+ turns to glass while scrolling; the coloured variants keep their own background (no scroll animation). */
5
+
6
+ .oc-site-header__actions { display: flex; align-items: center; gap: 0.5rem; flex: none; }
7
+ .oc-site-header__brand { white-space: nowrap; flex: none; }
8
+ .oc-site-header__nav > :is(a, button):not(.oc-btn) { white-space: nowrap; }
9
+
10
+ /* Centred menu: brand left, menu in the middle, actions right. */
11
+ @media (min-width: 861px) {
12
+ .oc-site-header--center .oc-site-header__inner { display: grid; grid-template-columns: 1fr auto 1fr; }
13
+ .oc-site-header--center .oc-site-header__brand { justify-self: start; }
14
+ .oc-site-header--center .oc-site-header__actions { justify-self: end; }
15
+ }
16
+
17
+ /* Dark: a solid dark bar with light text. */
18
+ .oc-site-header--dark {
19
+ animation: none;
20
+ color: var(--color-white-50);
21
+ background: var(--oc-dark-band, var(--color-duskblue-95));
22
+ }
23
+ .oc-site-header--dark :is(.oc-site-header__brand, .oc-site-header__nav a[aria-current="page"], .oc-site-header__nav a:not(.oc-btn):hover) { color: var(--color-white-50); }
24
+ .oc-site-header--dark .oc-site-header__nav a:not(.oc-btn) { color: oklch(from var(--color-white-50) l c h / 0.72); }
25
+
26
+ /* Floating: the bar as a rounded card with room around it. */
27
+ .oc-site-header:is(.oc-site-header--floating, .oc-site-header--glass) { animation: none; padding: 0.75rem; }
28
+ .oc-site-header--floating .oc-site-header__inner {
29
+ block-size: 4rem;
30
+ padding-inline: 1rem 0.75rem;
31
+ border: 1px solid var(--oc-border);
32
+ border-radius: 18px;
33
+ background: var(--oc-background);
34
+ box-shadow: 0 16px 36px -22px oklch(from var(--color-black-60) l c h / 0.45);
35
+ }
36
+
37
+ /* Glass: a clear liquid-glass pill over a photo or a hero (look in media-glass-liquid.css). */
38
+ .oc-site-header--glass .oc-site-header__inner { block-size: 3.75rem; padding-inline: 1.1rem 0.6rem; border-radius: 999px; }
39
+ .oc-site-header--glass :is(.oc-site-header__brand, .oc-site-header__nav a:not(.oc-btn)) { color: light-dark(var(--color-black-95), var(--color-white-50)); text-shadow: 0 1px 2px light-dark(oklch(100% 0 0deg / 0.5), oklch(0% 0 0deg / 0.4)); }
40
+
41
+ /* ---- Footer variants ---- */
42
+ /* Centred: brand, one row of links, social icons and the bottom line, all centred. */
43
+ .oc-site-footer--center { text-align: center; }
44
+ .oc-site-footer--center :is(.oc-site-footer__about, .oc-site-footer__bottom) { justify-items: center; justify-content: center; }
45
+ .oc-site-footer--center .oc-site-footer__about { display: grid; gap: 1rem; }
46
+ .oc-site-footer--center .oc-site-footer__about p { margin: 0; max-inline-size: 34rem; }
47
+ .oc-site-footer__row { display: flex; flex-wrap: wrap; justify-content: center; gap: 0.5rem 1.75rem; margin: 2rem 0 0; padding: 0; list-style: none; }
48
+ .oc-site-footer__social { display: flex; flex-wrap: wrap; gap: 0.5rem; margin: 0; padding: 0; list-style: none; }
49
+ .oc-site-footer--center .oc-site-footer__social { justify-content: center; margin-block-start: 1.5rem; }
50
+ .oc-site-footer__social a {
51
+ display: grid;
52
+ place-items: center;
53
+ inline-size: 2.25rem;
54
+ block-size: 2.25rem;
55
+ border: 1px solid var(--oc-border);
56
+ border-radius: 999px;
57
+ transition: border-color 0.2s, color 0.2s;
58
+ }
59
+ .oc-site-footer__social a:hover { border-color: var(--oc-primary); color: var(--oc-primary); }
60
+ .oc-site-footer__social :is(svg, iconify-icon) { inline-size: 1.1rem; block-size: 1.1rem; font-size: 1.1rem; }
61
+
62
+ /* Dark: light text on a dark band, the same deep blue in light and dark mode (--oc-dark-band), no top border. */
63
+ .oc-site-footer--dark { border-block-start: 0; color: oklch(from var(--color-white-50) l c h / 0.7); background: var(--oc-dark-band, var(--color-duskblue-95)); }
64
+ .oc-site-footer--dark :is(.oc-site-footer__title, .oc-site-header__brand, a:hover) { color: var(--color-white-50); }
65
+ .oc-site-footer--dark :is(.oc-site-footer__bottom, .oc-site-footer__social a) { border-color: oklch(from var(--color-white-50) l c h / 0.16); }
66
+
67
+ /* Newsletter: a sign-up field in the brand column. */
68
+ .oc-site-footer__signup { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-block-start: 1.25rem; max-inline-size: 24rem; }
69
+ .oc-site-footer__signup .oc-input { flex: 1 1 12rem; min-inline-size: 0; }
70
+ .oc-site-footer--dark .oc-site-footer__signup .oc-input { color: var(--color-white-50); background: oklch(from var(--color-white-50) l c h / 0.08); border-color: oklch(from var(--color-white-50) l c h / 0.2); }
@@ -3,7 +3,7 @@
3
3
  -> sizes (--sm, --lg) -> full width
4
4
  Prose: typography for long-form text from a CMS, blog or help page, where the HTML has no classes of its own.
5
5
  Every rule is scoped with :where(), so a utility class on an element inside still wins. Spacing is in em, so --sm and --lg
6
- scale the whole text. Based on the help and maintenance pages of the Korrekt app. */
6
+ scale the whole text. */
7
7
 
8
8
  .oc-prose {
9
9
  max-inline-size: 68ch;
package/dist/showcase.css CHANGED
@@ -30,6 +30,9 @@
30
30
  background: var(--oc-gradient-brand);
31
31
  -webkit-background-clip: text;
32
32
  background-clip: text;
33
+ /* The gradient only paints the box: keep italic or tight last letters inside it without moving the text. */
34
+ padding-inline-end: 0.08em;
35
+ margin-inline-end: -0.08em;
33
36
  color: transparent;
34
37
  }
35
38
  .oc-text-accent { background-image: linear-gradient(100deg, var(--oc-accent-from), var(--oc-accent-to)); }
@@ -129,17 +132,44 @@
129
132
  .oc-lead { margin: 0 0 1rem; color: var(--oc-muted-foreground); font-size: 1.15rem; line-height: 1.65; max-inline-size: 42rem; text-wrap: pretty; }
130
133
  .oc-split { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 24rem), 1fr)); gap: 3rem; align-items: center; }
131
134
 
132
- /* Code window: editor frame with traffic lights and a file name (always dark, like an editor) */
135
+ /* Code window: editor frame with traffic lights and a file name. It follows the page theme: a light frame and light
136
+ syntax colors in light mode, the Omnia Dark editor colors in dark mode. --dark keeps it dark on a light page (the
137
+ hero blocks, where it reads as an editor screenshot). */
133
138
  .oc-code-window {
134
139
  position: relative;
135
140
  border: 1px solid var(--oc-border-strong);
136
141
  border-radius: var(--oc-radius-lg);
137
- background: oklch(from var(--color-shallowblue-60) l c h / 0.9);
142
+ background: light-dark(var(--oc-card), oklch(from var(--color-shallowblue-60) l c h / 0.9));
138
143
  box-shadow: 0 40px 80px -30px oklch(from var(--color-black-50) l c h / 0.6);
139
144
  backdrop-filter: blur(12px);
140
145
  overflow: hidden;
141
- color-scheme: dark;
146
+ /* Syntax colors (oc-code-syntax.css) for both themes; the code sits on the window surface. */
147
+ --oc-syntax-bg: transparent;
148
+ --oc-syntax-bg-preview: transparent;
149
+ --oc-syntax-fg: light-dark(var(--color-shallowblue-60), var(--color-silver-60));
150
+ --oc-syntax-comment: light-dark(var(--color-gray-60), var(--color-purple-2));
151
+ --oc-syntax-string: light-dark(var(--color-green-80), var(--color-green-2));
152
+ --oc-syntax-keyword: light-dark(var(--color-purple-70), var(--color-purple-2));
153
+ --oc-syntax-variable: light-dark(var(--color-skycyan-90), var(--color-skycyan-30));
154
+ --oc-syntax-function: light-dark(var(--color-skycyan-90), var(--color-skycyan-60));
155
+ --oc-syntax-number: light-dark(var(--color-peru-80), var(--color-peru-40));
156
+ --oc-syntax-operator: light-dark(var(--color-red-70), var(--color-red-50));
157
+ --oc-syntax-constant: light-dark(var(--color-skycyan-90), var(--color-skycyan-70));
158
+ --oc-syntax-storage: light-dark(var(--color-green-80), var(--color-green-2));
159
+ --oc-syntax-tag: light-dark(var(--color-brown-70), var(--color-brown-10));
160
+ --oc-syntax-attr: light-dark(var(--color-skycyan-90), var(--color-skycyan-70));
161
+ --oc-syntax-prop: light-dark(var(--color-green-80), var(--color-green-2));
162
+ --oc-syntax-value: light-dark(var(--color-teal-80), var(--color-teal-10));
163
+ --oc-syntax-cls: light-dark(var(--color-darkgoldenrod-80), var(--color-wheat-70));
164
+ --oc-syntax-id: light-dark(var(--color-skycyan-90), var(--color-skycyan-70));
165
+ --oc-syntax-unit: light-dark(var(--color-peru-80), var(--color-peru-40));
166
+ --oc-syntax-key: light-dark(var(--color-brown-70), var(--color-brown-10));
167
+ --oc-code-block-line-number-color: light-dark(var(--color-gray-40), oklch(from var(--color-white-2) l c h / 0.25));
142
168
  }
169
+ .oc-code-window--dark { color-scheme: dark; box-shadow: 0 40px 80px -30px oklch(from var(--color-black-50) l c h / 0.6); }
170
+ .oc-code-window:not(.oc-code-window--dark) { box-shadow: 0 24px 60px -30px oklch(from var(--color-black-50) l c h / 0.25); }
171
+ /* The code block inside follows the window instead of its own always-dark theme. */
172
+ .oc-code-window :is(.oc-code-block, pre.code-preview) { color-scheme: inherit; }
143
173
  .oc-code-window__bar { display: flex; align-items: center; gap: 0.45rem; padding: 0.8rem 1rem; border-block-end: 1px solid var(--oc-border); }
144
174
  .oc-code-window__bar > i { inline-size: 0.7rem; aspect-ratio: 1; border-radius: 50%; background: var(--color-red-50); }
145
175
  .oc-code-window__bar > i:nth-child(2) { background: var(--color-orange-60); }
@@ -240,19 +270,20 @@
240
270
  background: oklch(from var(--oc-accent-from) l c h / 0.14);
241
271
  color: light-dark(var(--oc-accent-from), oklch(from var(--oc-accent-from) max(l, 0.72) c h));
242
272
  }
243
- /* Pulse: the ring starts at --oc-status-dot-pulse-alpha of the accent and grows to --oc-status-dot-pulse-size.
273
+ /* Live dot in a pill (.oc-pill > .oc-status-dot): the ring starts at --oc-status-dot-pulse-alpha of the accent and grows
274
+ to --oc-status-dot-pulse-size. Scoped to the pill: a plain .oc-status-dot is the status light (parts/app-status-dot.css).
244
275
  --strong starts at the full accent with a wider ring. */
245
- .oc-status-dot {
276
+ .oc-pill .oc-status-dot {
246
277
  --oc-status-dot-pulse-alpha: 0.6;
247
278
  --oc-status-dot-pulse-size: 8px;
248
279
  inline-size: 0.5rem;
249
280
  aspect-ratio: 1;
250
281
  border-radius: 50%;
251
282
  background: var(--oc-accent-from);
252
- animation: oc-pulse 2s infinite;
283
+ animation: oc-status-dot-ring 2s infinite;
253
284
  }
254
- .oc-status-dot--strong { --oc-status-dot-pulse-alpha: 1; --oc-status-dot-pulse-size: 10px; }
255
- @keyframes oc-pulse {
285
+ .oc-pill .oc-status-dot--strong { --oc-status-dot-pulse-alpha: 1; --oc-status-dot-pulse-size: 10px; }
286
+ @keyframes oc-status-dot-ring {
256
287
  0% { box-shadow: 0 0 0 0 oklch(from var(--oc-accent-from) l c h / var(--oc-status-dot-pulse-alpha)); }
257
288
  70%, 100% { box-shadow: 0 0 0 var(--oc-status-dot-pulse-size) transparent; }
258
289
  }
@@ -413,5 +444,5 @@
413
444
 
414
445
  @media (prefers-reduced-motion: reduce) {
415
446
  .oc-backdrop::before, .oc-backdrop::after, .oc-backdrop > .oc-backdrop__blob,
416
- .oc-icon-tile--float, .oc-marquee__track, .oc-status-dot, .oc-reveal, .oc-site-header { animation: none; }
447
+ .oc-icon-tile--float, .oc-marquee__track, .oc-pill .oc-status-dot, .oc-reveal, .oc-site-header { animation: none; }
417
448
  }
package/manifest.json CHANGED
@@ -688,14 +688,6 @@
688
688
  "@birdycom/omniaui"
689
689
  ]
690
690
  },
691
- {
692
- "name": "column-menu",
693
- "file": "snippets/column-menu.html",
694
- "description": "Column visibility checklist in a popover; hides table columns with :has() - no JavaScript",
695
- "requires": [
696
- "@birdycom/omniaui"
697
- ]
698
- },
699
691
  {
700
692
  "name": "confirm-dialog",
701
693
  "file": "snippets/confirm-dialog.html",
@@ -1035,7 +1027,7 @@
1035
1027
  {
1036
1028
  "name": "data-table",
1037
1029
  "file": "snippets/data-table.html",
1038
- "description": "List table from Korrekt and OmniaDesk: search, sort, pages, columns, resize, drag, trash filter and bulk actions",
1030
+ "description": "List table for business apps: search, sort, pages, trash filter and bulk actions",
1039
1031
  "requires": [
1040
1032
  "@birdycom/omniaui"
1041
1033
  ]
@@ -1159,6 +1151,126 @@
1159
1151
  "requires": [
1160
1152
  "@birdycom/omniaui"
1161
1153
  ]
1154
+ },
1155
+ {
1156
+ "name": "avatar-picker",
1157
+ "file": "snippets/avatar-picker.html",
1158
+ "description": "Avatar or company logo with a preview, Upload and Remove, and drag and drop",
1159
+ "requires": [
1160
+ "@birdycom/omniaui"
1161
+ ]
1162
+ },
1163
+ {
1164
+ "name": "barcode",
1165
+ "file": "snippets/barcode.html",
1166
+ "description": "EAN-13, EAN-8, UPC-A or Code 128 drawn as SVG from a value, no dependency",
1167
+ "requires": [
1168
+ "@birdycom/omniaui"
1169
+ ]
1170
+ },
1171
+ {
1172
+ "name": "date-field",
1173
+ "file": "snippets/date-field.html",
1174
+ "description": "Native date input with a calendar icon zone and the ISO week (Week 45) next to it",
1175
+ "requires": [
1176
+ "@birdycom/omniaui"
1177
+ ]
1178
+ },
1179
+ {
1180
+ "name": "form-density",
1181
+ "file": "snippets/form-density.html",
1182
+ "description": "Compact forms: .oc-density-compact makes every control in it 2rem high instead of 2.5rem",
1183
+ "requires": [
1184
+ "@birdycom/omniaui"
1185
+ ]
1186
+ },
1187
+ {
1188
+ "name": "form-section",
1189
+ "file": "snippets/form-section.html",
1190
+ "description": "Collapsible part of a long form with an icon, title, hint and status; red edge while a field inside is invalid",
1191
+ "requires": [
1192
+ "@birdycom/omniaui"
1193
+ ]
1194
+ },
1195
+ {
1196
+ "name": "meter-row",
1197
+ "file": "snippets/meter-row.html",
1198
+ "description": "Usage line with a label, the amount as text, a colored bar and when it resets",
1199
+ "requires": [
1200
+ "@birdycom/omniaui"
1201
+ ]
1202
+ },
1203
+ {
1204
+ "name": "name-card",
1205
+ "file": "snippets/name-card.html",
1206
+ "description": "Collapsible card with a name, rename pencil, amount and delete button; nests for titles and parts",
1207
+ "requires": [
1208
+ "@birdycom/omniaui"
1209
+ ]
1210
+ },
1211
+ {
1212
+ "name": "gallery",
1213
+ "file": "snippets/gallery.html",
1214
+ "description": "Product photos with thumbnails: one large photo and a row (or column) of thumbnails. No JavaScript",
1215
+ "requires": [
1216
+ "@birdycom/omniaui"
1217
+ ]
1218
+ },
1219
+ {
1220
+ "name": "price",
1221
+ "file": "snippets/price.html",
1222
+ "description": "A price with old price, discount, VAT note and the amount incl. or excl. VAT",
1223
+ "requires": [
1224
+ "@birdycom/omniaui"
1225
+ ]
1226
+ },
1227
+ {
1228
+ "name": "quantity",
1229
+ "file": "snippets/quantity.html",
1230
+ "description": "Number field with minus and plus buttons for amounts in carts, orders and stock",
1231
+ "requires": [
1232
+ "@birdycom/omniaui"
1233
+ ]
1234
+ },
1235
+ {
1236
+ "name": "rating",
1237
+ "file": "snippets/rating.html",
1238
+ "description": "Star rating: show a score with halves, or let people pick 1 to 5 stars. No JavaScript",
1239
+ "requires": [
1240
+ "@birdycom/omniaui"
1241
+ ]
1242
+ },
1243
+ {
1244
+ "name": "spec-list",
1245
+ "file": "snippets/spec-list.html",
1246
+ "description": "Technical specifications in named groups: label and value rows, two columns when there is room",
1247
+ "requires": [
1248
+ "@birdycom/omniaui"
1249
+ ]
1250
+ },
1251
+ {
1252
+ "name": "stock",
1253
+ "file": "snippets/stock.html",
1254
+ "description": "Stock status with a colored dot: in stock, low, out of stock or on order",
1255
+ "requires": [
1256
+ "@birdycom/omniaui"
1257
+ ]
1258
+ },
1259
+ {
1260
+ "name": "ribbon",
1261
+ "file": "snippets/ribbon.html",
1262
+ "description": "A diagonal band across a corner of a card, image or document: Paid, New, Sale. Pick the corner and the tone",
1263
+ "requires": [
1264
+ "@birdycom/omniaui"
1265
+ ]
1266
+ },
1267
+ {
1268
+ "name": "lazy",
1269
+ "file": "snippets/lazy.html",
1270
+ "description": "Images that load when they come near the screen, with a shimmer or a blurred preview until they are there, and sections drawn only when needed",
1271
+ "requires": [
1272
+ "@birdycom/omniaui"
1273
+ ]
1162
1274
  }
1163
1275
  ]
1164
1276
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@birdycom/omniaui",
3
- "version": "0.12.4",
3
+ "version": "0.12.6",
4
4
  "description": "OmniaUI components: CSS-first UI built on native HTML (dialog, popover, details) with light-dark() theming — no JavaScript required. Includes the omniaui CLI (add/list).",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -30,6 +30,9 @@
30
30
  "./snippets/*": "./snippets/*",
31
31
  "./data-table.js": "./dist/data-table.js",
32
32
  "./carousel.js": "./dist/carousel.js",
33
+ "./barcode.js": "./dist/barcode.js",
34
+ "./form-extras.js": "./dist/form-extras.js",
35
+ "./lazy.js": "./dist/lazy.js",
33
36
  "./manifest.json": "./manifest.json",
34
37
  "./package.json": "./package.json"
35
38
  },
@@ -57,7 +60,7 @@
57
60
  "omniaui": "./bin/omniaui.mjs"
58
61
  },
59
62
  "peerDependencies": {
60
- "@birdycom/omniacss": "^0.12.4"
63
+ "@birdycom/omniacss": "^0.12.6"
61
64
  },
62
65
  "peerDependenciesMeta": {
63
66
  "@birdycom/omniacss": {
@@ -11,7 +11,7 @@
11
11
  <div class="oc-accordion oc-accordion--accent">
12
12
  <details class="oc-accordion-item" name="oc-accent-demo" open>
13
13
  <summary class="oc-accordion-trigger">Customer details <span class="oc-accordion-accent-meta">Complete</span></summary>
14
- <div class="oc-accordion-content">Ada Lovelace, Main street 12, Ghent.</div>
14
+ <div class="oc-accordion-content">Contact 1, Main street 12, Ghent.</div>
15
15
  </details>
16
16
  <details class="oc-accordion-item oc-accent--violet" name="oc-accent-demo">
17
17
  <summary class="oc-accordion-trigger">Materials <span class="oc-accordion-accent-meta">4 items</span></summary>
@@ -13,10 +13,10 @@
13
13
  -->
14
14
  <div style="display:grid;gap:1.25rem;inline-size:100%;max-inline-size:40rem">
15
15
  <ul class="oc-attachment-list" aria-label="Files in this message">
16
- <li><a class="oc-attachment oc-attachment--pdf" href="#quote.pdf" download><span class="oc-attachment__icon" aria-hidden="true">PDF</span><span class="oc-attachment__body"><span class="oc-attachment__name">Quote kitchen Dewitte v2.pdf</span><span class="oc-attachment__size">1.2 MB</span></span></a></li>
17
- <li><a class="oc-attachment oc-attachment--sheet" href="#measurements.xlsx" download><span class="oc-attachment__icon" aria-hidden="true">XLS</span><span class="oc-attachment__body"><span class="oc-attachment__name">Measurements ground floor.xlsx</span><span class="oc-attachment__size">86 KB</span></span></a></li>
18
- <li><a class="oc-attachment oc-attachment--doc" href="#contract.docx" download><span class="oc-attachment__icon" aria-hidden="true">DOC</span><span class="oc-attachment__body"><span class="oc-attachment__name">Contract addendum.docx</span><span class="oc-attachment__size">240 KB</span></span></a></li>
19
- <li><a class="oc-attachment oc-attachment--zip" href="#plans.zip" download><span class="oc-attachment__icon" aria-hidden="true">ZIP</span><span class="oc-attachment__body"><span class="oc-attachment__name">Plans architect.zip</span><span class="oc-attachment__size">14.8 MB</span></span></a></li>
16
+ <li><a class="oc-attachment oc-attachment--pdf" href="#quote.pdf"><span class="oc-attachment__icon" aria-hidden="true">PDF</span><span class="oc-attachment__body"><span class="oc-attachment__name">Quote kitchen Project 1 v2.pdf</span><span class="oc-attachment__size">1.2 MB</span></span></a></li>
17
+ <li><a class="oc-attachment oc-attachment--sheet" href="#measurements.xlsx"><span class="oc-attachment__icon" aria-hidden="true">XLS</span><span class="oc-attachment__body"><span class="oc-attachment__name">Measurements ground floor.xlsx</span><span class="oc-attachment__size">86 KB</span></span></a></li>
18
+ <li><a class="oc-attachment oc-attachment--doc" href="#contract.docx"><span class="oc-attachment__icon" aria-hidden="true">DOC</span><span class="oc-attachment__body"><span class="oc-attachment__name">Contract addendum.docx</span><span class="oc-attachment__size">240 KB</span></span></a></li>
19
+ <li><a class="oc-attachment oc-attachment--zip" href="#plans.zip"><span class="oc-attachment__icon" aria-hidden="true">ZIP</span><span class="oc-attachment__body"><span class="oc-attachment__name">Plans architect.zip</span><span class="oc-attachment__size">14.8 MB</span></span></a></li>
20
20
  </ul>
21
21
 
22
22
  <a class="oc-attachment-image" href="#worktop-sample.jpg">
@@ -0,0 +1,40 @@
1
+ <!-- OmniaUI component: avatar-picker - Avatar or company logo with a preview, Upload and Remove, and drag and drop. Needs @birdycom/omniaui CSS.
2
+ File: packages/ui/snippets/avatar-picker.html | Map: avatar picker demo markup. Behaviour: form-extras.js.
3
+ Class API (modifiers are extra classes / attributes on the same element):
4
+ .oc-avatar-picker: Preview and text side by side.
5
+ label.oc-avatar-picker__preview: The preview; clicking it opens the file dialog (the file input fills it, invisible).
6
+ [data-oc-initials]: Shown until an image is picked.
7
+ [data-oc-avatar-upload] / [data-oc-avatar-remove]: Open the dialog / go back to the initials. Remove only shows while there is an image.
8
+ .oc-avatar-picker--square / --logo: Rounded square; --logo fits the whole image on white (no crop).
9
+ oc-avatar-change: Event with detail.file (the File, or null after Remove).
10
+ -->
11
+ <div style="display:grid;gap:1.5rem">
12
+ <div class="oc-avatar-picker">
13
+ <label class="oc-avatar-picker__preview" for="oc-avatar-me" title="Choose an image">
14
+ <span data-oc-initials>C1</span>
15
+ <input class="oc-avatar-picker__file" id="oc-avatar-me" type="file" accept="image/*">
16
+ </label>
17
+ <div class="oc-avatar-picker__text">
18
+ <span class="oc-avatar-picker__label">Profile picture</span>
19
+ <span class="oc-avatar-picker__hint">PNG or JPG, at least 256 × 256 px. Drop an image on the circle.</span>
20
+ <div class="oc-avatar-picker__actions">
21
+ <button type="button" class="oc-btn oc-btn--outline oc-btn--sm" data-oc-avatar-upload><iconify-icon icon="mdi:upload" aria-hidden="true"></iconify-icon>Upload</button>
22
+ <button type="button" class="oc-btn oc-btn--ghost oc-btn--sm" data-oc-avatar-remove><iconify-icon icon="mdi:close" aria-hidden="true"></iconify-icon>Remove</button>
23
+ </div>
24
+ </div>
25
+ </div>
26
+ <div class="oc-avatar-picker oc-avatar-picker--square oc-avatar-picker--logo">
27
+ <label class="oc-avatar-picker__preview" for="oc-logo-company" title="Choose an image">
28
+ <span data-oc-initials>CO</span>
29
+ <input class="oc-avatar-picker__file" id="oc-logo-company" type="file" accept="image/*">
30
+ </label>
31
+ <div class="oc-avatar-picker__text">
32
+ <span class="oc-avatar-picker__label">Company logo</span>
33
+ <span class="oc-avatar-picker__hint">SVG or PNG on a transparent background.</span>
34
+ <div class="oc-avatar-picker__actions">
35
+ <button type="button" class="oc-btn oc-btn--outline oc-btn--sm" data-oc-avatar-upload><iconify-icon icon="mdi:upload" aria-hidden="true"></iconify-icon>Upload</button>
36
+ <button type="button" class="oc-btn oc-btn--ghost oc-btn--sm" data-oc-avatar-remove><iconify-icon icon="mdi:close" aria-hidden="true"></iconify-icon>Remove</button>
37
+ </div>
38
+ </div>
39
+ </div>
40
+ </div>
@@ -0,0 +1,20 @@
1
+ <!-- OmniaUI component: barcode - EAN-13, EAN-8, UPC-A or Code 128 drawn as SVG from a value, no dependency. Needs @birdycom/omniaui CSS.
2
+ File: packages/ui/snippets/barcode.html | Map: barcode demo markup. Behaviour: barcode.js.
3
+ Class API (modifiers are extra classes / attributes on the same element):
4
+ figure.oc-barcode[data-value]: The barcode. The script replaces its content with the SVG and the digits.
5
+ data-format: auto (default: 12–13 digits EAN-13, 7–8 EAN-8, 11 UPC-A, else Code 128), EAN13, EAN8, UPC or CODE128.
6
+ data-digits="false": Bars only, no digits.
7
+ .oc-barcode--hero / --compact: Large (product page, label) or small (table cell).
8
+ --oc-barcode-height: Height of the bars (4rem).
9
+ [data-error]: Set when the value cannot be encoded (wrong check digit, non-ASCII in Code 128); the reason shows instead of bars.
10
+ encodeBarcode(value, format): Exported: the module string and the text, for your own canvas or PDF.
11
+ barcodeFile(value, { format, digits, moduleWidth, height }): Exported: a print-ready SVG file as text.
12
+ The barcode maker (live preview, download) is the OmniaUI Pro block packages/pro/blocks/barcode-maker.html.
13
+ -->
14
+ <div style="display:flex;flex-wrap:wrap;gap:1.5rem;align-items:center">
15
+ <figure class="oc-barcode oc-barcode--hero" data-value="5400000000010"></figure>
16
+ <figure class="oc-barcode" data-value="SKU-000105"></figure>
17
+ <figure class="oc-barcode" data-value="96385074"></figure>
18
+ <figure class="oc-barcode oc-barcode--compact" data-value="012345678905" data-format="UPC" data-digits="false"></figure>
19
+ <figure class="oc-barcode" data-value="5400000000018"></figure>
20
+ </div>
@@ -11,11 +11,11 @@
11
11
  <textarea class="oc-chat-composer__input" id="oc-chat-input" name="message" rows="1" placeholder="Write a message…" required></textarea>
12
12
  <div class="oc-chat-composer__bar">
13
13
  <div class="oc-chat-composer__tools">
14
- <button type="button" class="oc-chat-composer__tool" aria-label="Attach a file">&#128206;</button>
14
+ <button type="button" class="oc-chat-composer__tool" aria-label="Attach a file"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M16.5 6v11.5a4 4 0 0 1-4 4a4 4 0 0 1-4-4V5a2.5 2.5 0 0 1 2.5-2.5A2.5 2.5 0 0 1 13.5 5v10.5a1 1 0 0 1-1 1a1 1 0 0 1-1-1V6H10v9.5a2.5 2.5 0 0 0 2.5 2.5a2.5 2.5 0 0 0 2.5-2.5V5a4 4 0 0 0-4-4a4 4 0 0 0-4 4v12.5a5.5 5.5 0 0 0 5.5 5.5a5.5 5.5 0 0 0 5.5-5.5V6z"/></svg></button>
15
15
  <button type="button" class="oc-chat-composer__tool" aria-label="Mention someone">@</button>
16
- <button type="button" class="oc-chat-composer__tool" aria-label="Insert emoji">&#128578;</button>
16
+ <button type="button" class="oc-chat-composer__tool" aria-label="Insert emoji"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M20 12a8 8 0 0 0-8-8a8 8 0 0 0-8 8a8 8 0 0 0 8 8a8 8 0 0 0 8-8m2 0a10 10 0 0 1-10 10A10 10 0 0 1 2 12A10 10 0 0 1 12 2a10 10 0 0 1 10 10m-12-3.5c0 .8-.7 1.5-1.5 1.5S7 9.3 7 8.5S7.7 7 8.5 7s1.5.7 1.5 1.5m7 0c0 .8-.7 1.5-1.5 1.5S14 9.3 14 8.5S14.7 7 15.5 7s1.5.7 1.5 1.5m-5 9.73c-1.75 0-3.29-.73-4.19-1.81L9.23 14c.45.72 1.52 1.23 2.77 1.23s2.32-.51 2.77-1.23l1.42 1.42c-.9 1.08-2.44 1.81-4.19 1.81"/></svg></button>
17
17
  </div>
18
18
  <span class="oc-chat-composer__hint">Enter for a new line</span>
19
- <button type="submit" class="oc-chat-composer__send" aria-label="Send">&#10148;</button>
19
+ <button type="submit" class="oc-chat-composer__send" aria-label="Send"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M2 21l21-9L2 3v7l15 2l-15 2z"/></svg></button>
20
20
  </div>
21
21
  </form>