@birdycom/omniaui 0.12.6 → 0.13.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.
@@ -0,0 +1,40 @@
1
+ /* File: packages/ui/src/parts/site-fly.css */
2
+ /* Map: fly in (only with fly.js) -> visible -> reduced motion -> product card badge -> code window tokens */
3
+ /* Cards that fly in when they scroll into view: they start from the middle of their row (--oc-fly-x, set by fly.js),
4
+ 70px lower, smaller and tilted, and land in their place. Without fly.js (html.oc-fly-ready is missing) or with
5
+ "reduce motion" everything simply stands in its place. Options: --oc-fly-step (offset per column), --oc-fly-stagger
6
+ (delay per column) and data-oc-fly-repeat (fly out again when leaving the view). The lift on hover uses translate, so it never fights the
7
+ transform of the fly-in. Also: a "New" badge in a product card name and short syntax classes for a code window. */
8
+
9
+ .oc-fly-ready .oc-reveal--fly {
10
+ /* --oc-fly-delay: set per card by fly.js from --oc-fly-stagger (delay per column). */
11
+ transition:
12
+ opacity 0.6s var(--oc-ease-out, ease) var(--oc-fly-delay, 0s),
13
+ transform 0.9s cubic-bezier(0.16, 1, 0.3, 1) var(--oc-fly-delay, 0s),
14
+ border-color 0.35s;
15
+ }
16
+ .oc-fly-ready .oc-reveal--fly:not(.is-visible) {
17
+ opacity: 0;
18
+ transform: perspective(900px) translate3d(var(--oc-fly-x, 0), 70px, 0) scale(0.9) rotateX(14deg);
19
+ }
20
+ @media (prefers-reduced-motion: reduce) {
21
+ .oc-fly-ready .oc-reveal--fly:not(.is-visible) { opacity: 1; transform: none; }
22
+ }
23
+
24
+ /* "New" after the name of a product card: <h3 class="oc-product-card__name">Product 1 <span class="oc-product-card__badge oc-pill">New</span></h3> */
25
+ .oc-product-card__badge {
26
+ margin-inline-start: 0.4rem;
27
+ vertical-align: middle;
28
+ font-family: var(--oc-font-sans);
29
+ font-size: 0.7rem;
30
+ letter-spacing: 0.04em;
31
+ text-transform: uppercase;
32
+ }
33
+
34
+ /* Short syntax classes for hand-written code in an .oc-code-window: keyword, string, constant, function, muted.
35
+ They take the colors of the window (--oc-syntax-*), so they follow light and dark mode and a theme can change them. */
36
+ .oc-code-window .oc-tok-k { color: var(--oc-syntax-keyword); }
37
+ .oc-code-window .oc-tok-s { color: var(--oc-syntax-string); }
38
+ .oc-code-window .oc-tok-c { color: var(--oc-syntax-constant); }
39
+ .oc-code-window .oc-tok-f { color: var(--oc-syntax-function); }
40
+ .oc-code-window .oc-tok-m { color: var(--oc-syntax-comment); }
@@ -0,0 +1,69 @@
1
+ /* File: packages/ui/src/parts/site-layout.css */
2
+ /* Map: page hero -> section head / more -> action row -> group title -> band -> meta line */
3
+ /* Page building blocks for a marketing or company site, on top of the showcase classes (.oc-section-pad, .oc-display):
4
+ the larger header of an inner page, a section head and a "see more" row, a row of buttons, a group title with a count,
5
+ a full-width band (for a stat strip) and a meta line (version · date). */
6
+
7
+ /* Inner page header: more room on top, less below, and a larger display title. */
8
+ .oc-section-pad--page { padding-block: clamp(4rem, 10vw, 7rem) clamp(2rem, 5vw, 3rem); }
9
+ .oc-display--page {
10
+ margin-block-end: 1.25rem;
11
+ font-size: clamp(2.5rem, 6.5vw, 4.75rem);
12
+ }
13
+
14
+ /* Head of a section (eyebrow, title, lead) and the row with a "see all" link under the section. */
15
+ .oc-section-head {
16
+ max-inline-size: 46rem;
17
+ margin-block-end: 3rem;
18
+ }
19
+ .oc-section-head--center {
20
+ margin-inline: auto;
21
+ text-align: center;
22
+ }
23
+ .oc-section-more { margin-block-start: 2.5rem; }
24
+
25
+ /* Row of buttons under a text: wraps on a narrow screen. */
26
+ .oc-actions {
27
+ display: flex;
28
+ flex-wrap: wrap;
29
+ gap: 0.75rem;
30
+ margin-block-start: 2rem;
31
+ }
32
+ .oc-actions--center { justify-content: center; }
33
+ .oc-actions--end { justify-content: flex-end; }
34
+ .oc-actions--tight { margin-block-start: 1rem; }
35
+
36
+ /* Title of a group of cards with the number of items in a pill: <h3 class="oc-group-title">Apps <span class="oc-group-title__count">8</span></h3> */
37
+ .oc-group-title {
38
+ display: flex;
39
+ align-items: center;
40
+ gap: 0.75rem;
41
+ margin: 0 0 1.5rem;
42
+ font-size: 1.4rem;
43
+ }
44
+ .oc-group-title__count {
45
+ padding: 0.1rem 0.6rem;
46
+ border-radius: 999px;
47
+ background: var(--oc-surface-strong);
48
+ color: var(--oc-muted-foreground);
49
+ font-size: 0.85rem;
50
+ font-weight: 500;
51
+ }
52
+
53
+ /* Band over the full width of the page with a line above and below; the content stays in its .oc-container.
54
+ Put a .oc-stat-strip--plain in it for a row of key figures. */
55
+ .oc-band {
56
+ border-block: 1px solid var(--oc-border);
57
+ background: var(--oc-band-bg, var(--oc-surface));
58
+ }
59
+
60
+ /* Meta line under a page title: version · date, author. */
61
+ .oc-meta {
62
+ margin: 0 0 1rem;
63
+ color: var(--oc-subtle-foreground, var(--oc-muted-foreground));
64
+ font-size: 0.9rem;
65
+ }
66
+
67
+ /* Muted and subtle text from the theme tokens, for running text inside a panel, a spotlight or a split. */
68
+ .oc-text-muted { color: var(--oc-muted-foreground); }
69
+ .oc-text-subtle { color: var(--oc-subtle-foreground, var(--oc-muted-foreground)); }
@@ -0,0 +1,29 @@
1
+ /* File: packages/ui/src/parts/site-panel.css */
2
+ /* Map: panel -> glow (start / end / both) */
3
+ /* A framed panel on the page (a form, a call to action, a feature with a list): card background, strong border, large
4
+ radius, and room above it when a link jumps to it (#form). --glow adds a soft radial light in a corner:
5
+ --oc-panel-glow-color (brand 1) and --oc-panel-glow-at (top left); --glow-end puts it top right, --glow-both adds a
6
+ second light (brand 3) bottom right. */
7
+
8
+ .oc-panel {
9
+ --oc-panel-glow-color: var(--oc-brand-1);
10
+ --oc-panel-glow-at: 0% 0%;
11
+ position: relative;
12
+ padding: var(--oc-panel-padding, clamp(1.5rem, 4vw, 2.5rem));
13
+ border: 1px solid var(--oc-border-strong);
14
+ border-radius: calc(var(--oc-radius-lg, 1.25rem) * 1.2);
15
+ background: var(--oc-card);
16
+ scroll-margin-top: var(--oc-scroll-offset, 6rem);
17
+ }
18
+ .oc-panel--glow {
19
+ background:
20
+ radial-gradient(30rem circle at var(--oc-panel-glow-at), oklch(from var(--oc-panel-glow-color) l c h / 0.12), transparent 60%),
21
+ var(--oc-card);
22
+ }
23
+ .oc-panel--glow-end { --oc-panel-glow-at: 100% 0%; }
24
+ .oc-panel--glow-both {
25
+ background:
26
+ radial-gradient(40rem circle at 0% 0%, oklch(from var(--oc-brand-1) l c h / 0.18), transparent 55%),
27
+ radial-gradient(40rem circle at 100% 100%, oklch(from var(--oc-brand-3) l c h / 0.2), transparent 55%),
28
+ var(--oc-card);
29
+ }
@@ -134,3 +134,15 @@
134
134
  @media (prefers-reduced-motion: reduce) {
135
135
  .oc-prose :where(a) { transition: none; }
136
136
  }
137
+
138
+ /* ---- Legal text (terms, privacy, cookies): narrower, muted body text in a slightly larger size, headings and strong
139
+ in the foreground, disc lists with a gap, links in brand 1. A table in an .oc-table-wrap scrolls on a phone. ---- */
140
+ .oc-prose--legal { max-inline-size: 46rem; color: var(--oc-muted-foreground); font-size: 1.0625rem; }
141
+ .oc-prose--legal :where(h2) { margin-block-start: 2.5rem; font-size: 1.35rem; color: var(--oc-foreground); }
142
+ .oc-prose--legal :where(h3) { margin-block-start: 1.5rem; font-size: 1.05rem; color: var(--oc-foreground); }
143
+ .oc-prose--legal :where(strong, b) { color: var(--oc-foreground); }
144
+ .oc-prose--legal :where(ul) { display: grid; gap: 0.4rem; padding-inline-start: 1.25rem; list-style: disc; }
145
+ .oc-prose--legal :where(ul > li) { margin-block-start: 0; }
146
+ .oc-prose--legal :where(a) { color: var(--oc-brand-1); font-weight: inherit; text-decoration-color: currentColor; text-underline-offset: 3px; }
147
+ .oc-prose--legal :where(a:hover) { color: var(--oc-foreground); }
148
+ .oc-prose--legal :where(table) { font-size: 0.93rem; }
@@ -0,0 +1,44 @@
1
+ /* File: packages/ui/src/parts/utility-hover.css */
2
+ /* Map: smooth timing -> lift / grow / glow / none -> steady hover area -> reduced motion */
3
+ /* Hover effect of a card, chosen with one class on the card (any card: .oc-card, product, feature, link, price, project …):
4
+ .oc-hover-lift moves up (--oc-hover-lift, 6px) with a soft shadow; what a product card does by default
5
+ .oc-hover-grow grows a little in place (--oc-hover-scale, 1.02)
6
+ .oc-hover-glow stays in place, with a glow in the accent color
7
+ .oc-hover-none no movement (also turns off the default lift of a product or price card)
8
+ They share one smooth curve: --oc-hover-duration (0.45s) and --oc-hover-ease. This file loads after the cards, so the class wins. */
9
+
10
+ :is(.oc-hover-lift, .oc-hover-grow, .oc-hover-glow, .oc-product-card) {
11
+ transition:
12
+ translate var(--oc-hover-duration, 0.45s) var(--oc-hover-ease, cubic-bezier(0.22, 1, 0.36, 1)),
13
+ scale var(--oc-hover-duration, 0.45s) var(--oc-hover-ease, cubic-bezier(0.22, 1, 0.36, 1)),
14
+ box-shadow var(--oc-hover-duration, 0.45s) var(--oc-hover-ease, cubic-bezier(0.22, 1, 0.36, 1)),
15
+ border-color 0.35s,
16
+ background-color 0.35s,
17
+ --oc-glow 0.35s;
18
+ }
19
+ .oc-product-card:hover { translate: 0 calc(-1 * var(--oc-hover-lift, 6px)); }
20
+ .oc-hover-lift:hover {
21
+ translate: 0 calc(-1 * var(--oc-hover-lift, 6px));
22
+ box-shadow: 0 1.25rem 2.5rem -1.25rem oklch(from var(--color-black-60) l c h / 0.35);
23
+ }
24
+ .oc-hover-grow:hover { translate: none; scale: var(--oc-hover-scale, 1.02); }
25
+ .oc-hover-glow:hover {
26
+ translate: none;
27
+ box-shadow:
28
+ 0 0 0 1px oklch(from var(--oc-accent-from, var(--oc-primary)) l c h / 0.4),
29
+ 0 0.75rem 2.5rem -0.75rem oklch(from var(--oc-accent-from, var(--oc-primary)) l c h / 0.5);
30
+ }
31
+ .oc-hover-none:hover { translate: none; scale: none; }
32
+
33
+ /* While a card moves up, the strip it leaves keeps the hover, so a mouse at the bottom edge does not make it bounce. */
34
+ :where(.oc-hover-lift) { position: relative; }
35
+ :is(.oc-hover-lift, .oc-product-card:not([class*="oc-hover-"])):hover::before {
36
+ content: "";
37
+ position: absolute;
38
+ inset: 0 0 calc(-1 * var(--oc-hover-lift, 6px) - 2px);
39
+ z-index: -1;
40
+ }
41
+
42
+ @media (prefers-reduced-motion: reduce) {
43
+ :is(.oc-hover-lift, .oc-hover-grow, .oc-product-card):hover { translate: none; scale: none; }
44
+ }
package/dist/showcase.css CHANGED
@@ -54,7 +54,8 @@
54
54
  /* border-box: the gradient also runs under the transparent 1px border of .oc-btn; from the padding box it would
55
55
  repeat in that border and show a thin stripe of the other end's color on both sides. */
56
56
  background: linear-gradient(100deg, var(--oc-accent-from), var(--oc-accent-to)) border-box;
57
- color: var(--color-white-50);
57
+ /* --oc-btn-gradient-ink: dark text on a light accent (set it with the accent). */
58
+ color: var(--oc-btn-gradient-ink, var(--color-white-50));
58
59
  box-shadow: 0 10px 30px -10px var(--oc-accent-to);
59
60
  transition: transform 0.2s var(--oc-ease-out), box-shadow 0.2s;
60
61
  }
@@ -81,7 +82,8 @@
81
82
 
82
83
  /* Backdrop: slowly drifting color blobs + fine grid behind the page.
83
84
  <div class="oc-backdrop" aria-hidden="true"><i class="oc-backdrop__blob"></i><i class="oc-backdrop__grid"></i></div>
84
- .oc-backdrop--local keeps it inside a positioned section instead of the viewport. */
85
+ .oc-backdrop--local keeps it inside a positioned section instead of the viewport.
86
+ A theme sets the colors: --oc-backdrop-1, -2, -3 (the blobs), --oc-backdrop-3-opacity and --oc-backdrop-grid (the lines). */
85
87
  .oc-backdrop {
86
88
  position: fixed;
87
89
  inset: 0;
@@ -102,24 +104,27 @@
102
104
  opacity: var(--oc-backdrop-opacity, 0.35);
103
105
  animation: oc-drift 24s ease-in-out infinite alternate;
104
106
  }
105
- .oc-backdrop::before { background: var(--color-blue-40); inset-block-start: -18rem; inset-inline-start: -12rem; }
106
- .oc-backdrop::after { background: var(--color-purple-30); inset-block-start: 10rem; inset-inline-end: -16rem; animation-delay: -8s; }
107
- .oc-backdrop > .oc-backdrop__blob { background: var(--color-teal-40); inset-block-end: -20rem; inset-inline-start: 30%; animation-delay: -16s; opacity: calc(var(--oc-backdrop-opacity, 0.35) * 0.6); }
107
+ .oc-backdrop::before { background: var(--oc-backdrop-1, var(--color-blue-40)); inset-block-start: -18rem; inset-inline-start: -12rem; }
108
+ .oc-backdrop::after { background: var(--oc-backdrop-2, var(--color-purple-30)); inset-block-start: 10rem; inset-inline-end: -16rem; animation-delay: -8s; }
109
+ .oc-backdrop > .oc-backdrop__blob { background: var(--oc-backdrop-3, var(--color-teal-40)); inset-block-end: -20rem; inset-inline-start: 30%; animation-delay: -16s; opacity: var(--oc-backdrop-3-opacity, calc(var(--oc-backdrop-opacity, 0.35) * 0.6)); }
108
110
  .oc-backdrop > .oc-backdrop__grid {
109
111
  position: absolute;
110
112
  inset: 0;
111
113
  background-image:
112
- linear-gradient(oklch(from var(--oc-foreground) l c h / 0.05) 1px, transparent 1px),
113
- linear-gradient(90deg, oklch(from var(--oc-foreground) l c h / 0.05) 1px, transparent 1px);
114
+ linear-gradient(var(--oc-backdrop-grid, oklch(from var(--oc-foreground) l c h / 0.05)) 1px, transparent 1px),
115
+ linear-gradient(90deg, var(--oc-backdrop-grid, oklch(from var(--oc-foreground) l c h / 0.05)) 1px, transparent 1px);
114
116
  background-size: 64px 64px;
115
117
  mask-image: radial-gradient(ellipse at 50% 0%, black 20%, transparent 70%);
116
118
  }
117
119
  @keyframes oc-drift { to { transform: translate(6rem, 4rem) scale(1.15); } }
118
120
 
119
121
  /* Container, section spacing and display headings */
120
- .oc-container { inline-size: min(100% - 2.5rem, var(--oc-container, 1200px)); margin-inline: auto; }
122
+ /* --oc-container is the width, --oc-container-gutter the room on each side on a narrower screen (1.25rem). */
123
+ .oc-container { inline-size: min(100% - 2 * var(--oc-container-gutter, 1.25rem), var(--oc-container, 1200px)); margin-inline: auto; }
121
124
  .oc-section-pad { padding-block: clamp(4rem, 9vw, 7rem); }
122
125
  .oc-section-pad--tight { padding-block: clamp(2rem, 5vw, 3.5rem); }
126
+ /* The hero of a home page: a little less room on top and below than a section. */
127
+ .oc-section-pad--hero { padding-block: clamp(3.5rem, 9vw, 7rem) clamp(3rem, 6vw, 5rem); }
123
128
  .oc-display {
124
129
  margin: 0 0 1.5rem;
125
130
  font-size: clamp(2.6rem, 5.6vw, 4.6rem);
@@ -129,8 +134,28 @@
129
134
  text-wrap: balance;
130
135
  }
131
136
  .oc-display--md { font-size: clamp(1.9rem, 4vw, 3rem); }
137
+ .oc-display--sm { font-size: clamp(1.5rem, 3vw, 2.1rem); }
132
138
  .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; }
133
- .oc-split { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 24rem), 1fr)); gap: 3rem; align-items: center; }
139
+ /* Larger and narrower, for a home hero. */
140
+ .oc-lead--lg { font-size: 1.2rem; max-inline-size: 36rem; }
141
+ .oc-split { display: grid; grid-template-columns: var(--oc-split-cols, repeat(auto-fit, minmax(min(100%, 24rem), 1fr))); gap: var(--oc-split-gap, 3rem); align-items: center; }
142
+ /* Ratio presets (one column up to 960px): --lead makes the text column wider, --media the image column.
143
+ Your own ratio: set --oc-split-cols in a class (and to 1fr in a media query for small screens). */
144
+ .oc-split--lead { --oc-split-cols: 1.25fr 1fr; }
145
+ .oc-split--media { --oc-split-cols: 1fr 1.15fr; }
146
+ .oc-split__media { display: grid; place-items: center; }
147
+ /* Columns aligned at the top (a fact list next to a long text). */
148
+ .oc-split--top { align-items: start; }
149
+ /* --oc-split-cols-wide: your own ratio that only applies above 960px, so the split still stacks on a phone. */
150
+ @media (min-width: 961px) {
151
+ .oc-split { grid-template-columns: var(--oc-split-cols-wide, var(--oc-split-cols, repeat(auto-fit, minmax(min(100%, 24rem), 1fr)))); }
152
+ }
153
+ @media (max-width: 960px) {
154
+ .oc-split--lead, .oc-split--media { --oc-split-cols: 1fr; }
155
+ /* The image (a product mark, a screenshot) comes first on a small screen, aligned at the start, and an xl tile shrinks. */
156
+ .oc-split__media { order: -1; justify-items: start; }
157
+ .oc-split__media .oc-icon-tile--xl { --oc-tile-size: 7rem; }
158
+ }
134
159
 
135
160
  /* Code window: editor frame with traffic lights and a file name. It follows the page theme: a light frame and light
136
161
  syntax colors in light mode, the Omnia Dark editor colors in dark mode. --dark keeps it dark on a light page (the
@@ -218,7 +243,12 @@
218
243
  @keyframes oc-float { 50% { translate: 0 -12px; } }
219
244
 
220
245
  /* Product / feature card with hover lift and accent glow */
221
- .oc-card-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, var(--oc-card-min, 17rem)), 1fr)); gap: 1.25rem; }
246
+ .oc-card-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, var(--oc-card-min, 17rem)), 1fr)); gap: var(--oc-card-gap, 1.25rem); }
247
+ /* Wide cards (projects, case studies): from 32rem, three in a row on a wide screen. */
248
+ .oc-card-grid--wide { --oc-card-min: 32rem; --oc-card-gap: 1.5rem; }
249
+ /* --fit: the cards stretch over the full width when there are fewer than fit in a row (auto-fit instead of auto-fill). */
250
+ .oc-card-grid--fit { grid-template-columns: repeat(auto-fit, minmax(min(100%, var(--oc-card-min, 17rem)), 1fr)); }
251
+ @media (min-width: 1280px) { .oc-card-grid--wide { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
222
252
  .oc-product-card {
223
253
  position: relative;
224
254
  display: flex;
@@ -232,7 +262,7 @@
232
262
  var(--oc-surface);
233
263
  color: var(--oc-foreground);
234
264
  text-decoration: none;
235
- transition: transform 0.35s var(--oc-ease-out), border-color 0.35s, --oc-glow 0.35s;
265
+ transition: translate 0.35s var(--oc-ease-out), border-color 0.35s, --oc-glow 0.35s;
236
266
  }
237
267
  @property --oc-glow {
238
268
  syntax: "<number>";
@@ -241,7 +271,8 @@
241
271
  }
242
272
  .oc-product-card:hover {
243
273
  --oc-glow: 0.22;
244
- transform: translateY(-6px);
274
+ /* The lift uses translate; a fly-in (.oc-reveal--fly) animates the transform, so both keep working. */
275
+ translate: 0 calc(-1 * var(--oc-hover-lift, 6px));
245
276
  border-color: oklch(from var(--oc-accent-from) l c h / 0.55);
246
277
  }
247
278
  .oc-product-card__top { display: flex; justify-content: space-between; align-items: flex-start; margin-block-end: 0.75rem; }
@@ -297,9 +328,17 @@
297
328
  }
298
329
  .oc-stat-strip__item { padding: 2rem 1.5rem; border-inline-start: 1px solid var(--oc-border); }
299
330
  .oc-stat-strip__item:first-child { border-inline-start: 0; }
300
- .oc-stat-strip__value { font-size: clamp(2.2rem, 4vw, 3rem); font-weight: 700; line-height: 1.1; }
331
+ .oc-stat-strip__value { font-family: var(--oc-font-display, inherit); font-size: clamp(2.2rem, 4vw, 3rem); font-weight: 700; line-height: 1.1; }
301
332
  .oc-stat-strip__suffix { color: var(--oc-brand-1); }
302
333
  .oc-stat-strip__label { display: block; color: var(--oc-muted-foreground); font-size: 0.92rem; }
334
+ /* Plain: no lines and no background of its own, for a strip inside an .oc-band (full width) or a card. */
335
+ .oc-stat-strip--plain { border-block: 0; background: none; }
336
+ /* Four figures on a narrower screen: two rows of two instead of three and one. */
337
+ @media (max-width: 960px) {
338
+ .oc-stat-strip:has(> .oc-stat-strip__item:nth-child(4):last-child) { grid-template-columns: 1fr 1fr; }
339
+ .oc-stat-strip:has(> .oc-stat-strip__item:nth-child(4):last-child) > .oc-stat-strip__item:nth-child(3) { border-inline-start: 0; }
340
+ .oc-stat-strip:has(> .oc-stat-strip__item:nth-child(4):last-child) > .oc-stat-strip__item:nth-child(n + 3) { border-block-start: 1px solid var(--oc-border); }
341
+ }
303
342
 
304
343
  /* Spotlight: large highlighted panel with copy + module list */
305
344
  .oc-spotlight {
@@ -317,8 +356,12 @@
317
356
  }
318
357
  .oc-spotlight__title { display: flex; align-items: center; gap: 1rem; margin: 1.25rem 0 0.5rem; font-size: clamp(2.2rem, 5vw, 3.5rem); line-height: 1.1; }
319
358
  .oc-spotlight__tagline { color: light-dark(var(--oc-accent-from), oklch(from var(--oc-accent-from) max(l, 0.72) c h)); font-size: 1.2rem; font-weight: 600; }
359
+ /* --oc-spotlight-cols: your own ratio above 960px (copy | modules); on a smaller screen the spotlight stacks. */
360
+ @media (min-width: 961px) {
361
+ .oc-spotlight { grid-template-columns: var(--oc-spotlight-cols, repeat(auto-fit, minmax(min(100%, 22rem), 1fr))); }
362
+ }
320
363
  .oc-spotlight__modules { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr)); gap: 0.75rem; margin: 0; padding: 0; list-style: none; }
321
- .oc-spotlight__modules > li { display: flex; align-items: center; gap: 0.75rem; padding: 0.9rem; border: 1px solid var(--oc-border); border-radius: var(--oc-radius-md); background: var(--oc-surface); font-size: 0.95rem; }
364
+ .oc-spotlight__modules > li { display: flex; align-items: center; gap: 0.75rem; padding: 0.9rem; border: 1px solid var(--oc-border); border-radius: var(--oc-radius-md); background: var(--oc-spotlight-module-bg, var(--oc-surface)); font-size: 0.95rem; }
322
365
 
323
366
  /* Steps: numbered process with a gradient tick (numbers come from a CSS counter) */
324
367
  .oc-steps { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr)); gap: 1.25rem; margin: 0; padding: 0; list-style: none; counter-reset: oc-step; }
@@ -351,6 +394,9 @@
351
394
  /* Tag list (tech stack) and check list */
352
395
  .oc-tag-list { display: flex; flex-wrap: wrap; gap: 0.6rem; margin: 0; padding: 0; list-style: none; }
353
396
  .oc-tag-list > li { padding: 0.55rem 1.1rem; border: 1px solid oklch(from var(--oc-accent-from) l c h / 0.35); border-radius: 999px; background: oklch(from var(--oc-accent-from) l c h / 0.1); font-family: var(--oc-font-mono); font-size: 0.88rem; }
397
+ /* Neutral chips (no accent color) and large chips in the display font, e.g. a strip of brand names. */
398
+ .oc-tag-list--neutral > li { border-color: var(--oc-border); background: var(--oc-surface); color: var(--oc-foreground); }
399
+ .oc-tag-list--lg > li { padding: 0.45rem 1rem; font-family: var(--oc-font-display, inherit); font-size: 1rem; font-weight: 700; letter-spacing: 0.02em; }
354
400
  .oc-check-list { display: grid; gap: 0.5rem; margin: 1.1rem 0 0; padding: 0; list-style: none; font-size: 0.93rem; }
355
401
  .oc-check-list > li { display: flex; gap: 0.5rem; align-items: baseline; }
356
402
  .oc-check-list > li::before { content: "✓"; color: var(--oc-brand-1); font-weight: 700; }
@@ -385,6 +431,8 @@
385
431
  .oc-site-header__nav a:not(.oc-btn):hover,
386
432
  .oc-site-header__nav a[aria-current="page"] { color: var(--oc-foreground); }
387
433
  .oc-site-header__toggle { display: none; padding: 0.5rem 0.7rem; border: 1px solid var(--oc-border); border-radius: var(--oc-radius-md); background: var(--oc-surface); color: var(--oc-foreground); cursor: pointer; }
434
+ /* The icon in the menu button is a block, so the button is as tall as a line of text (no inline gap under it). */
435
+ .oc-site-header__toggle :is(svg, img, iconify-icon, .oc-icon) { display: block; }
388
436
  @media (min-width: 861px) {
389
437
  /* Desktop: the popover nav is always visible in the header bar. */
390
438
  .oc-site-header__nav[popover] { display: flex; }
@@ -409,6 +457,8 @@
409
457
  backdrop-filter: blur(16px);
410
458
  }
411
459
  .oc-site-header__nav[popover] a:not(.oc-btn) { padding: 0.75rem 0; font-size: 1.1rem; }
460
+ /* The call to action (.oc-site-header__cta, a button) gets room above it in the open menu. */
461
+ .oc-site-header__cta { margin-block-start: 0.75rem; }
412
462
  }
413
463
 
414
464
  /* Site footer: brand column + link columns + bottom bar */
@@ -416,21 +466,33 @@
416
466
  margin-block-start: 4rem;
417
467
  padding-block-start: 4rem;
418
468
  border-block-start: 1px solid var(--oc-border);
419
- background: linear-gradient(to bottom, transparent, var(--oc-surface-strong));
469
+ background: var(--oc-footer-bg, linear-gradient(to bottom, transparent, var(--oc-surface-strong)));
420
470
  color: var(--oc-muted-foreground);
421
471
  font-size: 0.95rem;
472
+ container-type: inline-size;
422
473
  }
423
- .oc-site-footer__grid { display: grid; grid-template-columns: 1.6fr repeat(3, 1fr); gap: 2.5rem; }
474
+ /* Columns: --oc-footer-cols (about + 3 link columns). Footer narrower than 66rem (about a 1060px screen):
475
+ --oc-footer-cols-md columns (3) with the about column over the full width; narrower than 54rem: two columns.
476
+ Container queries on the footer, so a footer in a narrow column or a preview follows its own width. */
477
+ .oc-site-footer__grid { display: grid; grid-template-columns: var(--oc-footer-cols, 1.6fr repeat(3, 1fr)); gap: 2.5rem; }
424
478
  .oc-site-footer__about p { margin-block: 1rem 0; max-inline-size: 22rem; }
425
479
  .oc-site-footer__title { margin: 0 0 1rem; color: var(--oc-foreground); font-size: 0.95rem; font-weight: 700; }
426
480
  .oc-site-footer__list { display: flex; flex-direction: column; gap: 0.5rem; margin: 0; padding: 0; list-style: none; font-style: normal; }
427
481
  .oc-site-footer a { color: inherit; text-decoration: none; }
428
482
  .oc-site-footer a:hover { color: var(--oc-foreground); }
429
- .oc-site-footer__bottom { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 1rem; margin-block-start: 3rem; padding-block: 1.5rem; border-block-start: 1px solid var(--oc-border); font-size: 0.85rem; }
430
- @media (max-width: 860px) {
431
- .oc-site-footer__grid { grid-template-columns: 1fr 1fr; }
483
+ .oc-site-footer__bottom { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 1rem; margin-block-start: 3rem; padding-block: 1.5rem; border-block-start: 1px solid var(--oc-border); color: var(--oc-footer-bottom-color, inherit); font-size: 0.85rem; }
484
+ /* A second title further down in a column (e.g. "Tools" under the projects). */
485
+ .oc-site-footer__subtitle { margin-block-start: 1.75rem; }
486
+ /* Legal links in the bottom bar (Terms · Privacy · Cookies) and the build number after them. */
487
+ .oc-site-footer__legal { display: flex; flex-wrap: wrap; gap: 0.5rem 1.25rem; margin: 0; padding: 0; list-style: none; }
488
+ .oc-site-footer__build { opacity: 0.7; font-variant-numeric: tabular-nums; }
489
+ @container (max-width: 66rem) {
490
+ .oc-site-footer__grid { grid-template-columns: repeat(var(--oc-footer-cols-md, 3), minmax(0, 1fr)); }
432
491
  .oc-site-footer__about { grid-column: 1 / -1; }
433
492
  }
493
+ @container (max-width: 54rem) {
494
+ .oc-site-footer__grid { grid-template-columns: 1fr 1fr; }
495
+ }
434
496
 
435
497
  /* Reveal on scroll: elements slide in when they enter the viewport (scroll-driven, no JS; visible without support) */
436
498
  @supports (animation-timeline: view()) {
package/manifest.json CHANGED
@@ -1271,6 +1271,78 @@
1271
1271
  "requires": [
1272
1272
  "@birdycom/omniaui"
1273
1273
  ]
1274
+ },
1275
+ {
1276
+ "name": "panel",
1277
+ "file": "snippets/panel.html",
1278
+ "description": "A framed panel on the page for a form, a call to action or a feature: card background, strong border, large radius and an optional soft glow in a corner",
1279
+ "requires": [
1280
+ "@birdycom/omniaui"
1281
+ ]
1282
+ },
1283
+ {
1284
+ "name": "result",
1285
+ "file": "snippets/result.html",
1286
+ "description": "The outcome of an action in the place where the form was: an icon tile, a title, a short text and a button such as Send another message",
1287
+ "requires": [
1288
+ "@birdycom/omniaui"
1289
+ ]
1290
+ },
1291
+ {
1292
+ "name": "brand-mark",
1293
+ "file": "snippets/brand-mark.html",
1294
+ "description": "A logo from an icon and text: the mark with a glow in the brand color, a word mark with one part in the brand gradient and a small tagline",
1295
+ "requires": [
1296
+ "@birdycom/omniaui"
1297
+ ]
1298
+ },
1299
+ {
1300
+ "name": "page-sections",
1301
+ "file": "snippets/page-sections.html",
1302
+ "description": "Building blocks for the pages of a company site: the header of an inner page, a section head, a row of buttons, a group title with a count, a full-width band and a meta line",
1303
+ "requires": [
1304
+ "@birdycom/omniaui"
1305
+ ]
1306
+ },
1307
+ {
1308
+ "name": "feature-card",
1309
+ "file": "snippets/feature-card.html",
1310
+ "description": "Cards for services and features (icon tile, title, text) and cards that are links, in a grid whose column width you set",
1311
+ "requires": [
1312
+ "@birdycom/omniaui"
1313
+ ]
1314
+ },
1315
+ {
1316
+ "name": "icon-list",
1317
+ "file": "snippets/icon-list.html",
1318
+ "description": "Lists for a company site: rows with an icon in a frame, points with an icon tile, a title and a text, and a list with markers in the accent color",
1319
+ "requires": [
1320
+ "@birdycom/omniaui"
1321
+ ]
1322
+ },
1323
+ {
1324
+ "name": "shot-grid",
1325
+ "file": "snippets/shot-grid.html",
1326
+ "description": "A grid of screenshots with a caption that open in the lightbox, and a browser frame with a glow in the accent color and a product mark on it",
1327
+ "requires": [
1328
+ "@birdycom/omniaui"
1329
+ ]
1330
+ },
1331
+ {
1332
+ "name": "fly-in",
1333
+ "file": "snippets/fly-in.html",
1334
+ "description": "Cards that fly in from the middle of their row when they scroll into view, with a hover lift that keeps working, and a New badge in a product card",
1335
+ "requires": [
1336
+ "@birdycom/omniaui"
1337
+ ]
1338
+ },
1339
+ {
1340
+ "name": "hover-effects",
1341
+ "file": "snippets/hover-effects.html",
1342
+ "description": "Choose how a card reacts to the mouse: lift, grow, glow or stay still, with one smooth curve for all of them",
1343
+ "requires": [
1344
+ "@birdycom/omniaui"
1345
+ ]
1274
1346
  }
1275
1347
  ]
1276
1348
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@birdycom/omniaui",
3
- "version": "0.12.6",
3
+ "version": "0.13.0",
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",
@@ -33,6 +33,8 @@
33
33
  "./barcode.js": "./dist/barcode.js",
34
34
  "./form-extras.js": "./dist/form-extras.js",
35
35
  "./lazy.js": "./dist/lazy.js",
36
+ "./contact-form.js": "./dist/contact-form.js",
37
+ "./fly.js": "./dist/fly.js",
36
38
  "./manifest.json": "./manifest.json",
37
39
  "./package.json": "./package.json"
38
40
  },
@@ -60,7 +62,7 @@
60
62
  "omniaui": "./bin/omniaui.mjs"
61
63
  },
62
64
  "peerDependencies": {
63
- "@birdycom/omniacss": "^0.12.6"
65
+ "@birdycom/omniacss": "^0.13.0"
64
66
  },
65
67
  "peerDependenciesMeta": {
66
68
  "@birdycom/omniacss": {
@@ -0,0 +1,41 @@
1
+ <!-- OmniaUI component: brand-mark - A logo from an icon and text: the mark with a glow in the brand color, a word mark with one part in the brand gradient and a small tagline. Needs @birdycom/omniaui CSS.
2
+ File: packages/ui/snippets/brand-mark.html | Map: in the header -> sizes.
3
+ Class API (modifiers are extra classes / attributes on the same element):
4
+ .oc-brand-mark: The logo: a link or a span with the icon and the text side by side.
5
+ .oc-brand-mark__icon: The mark (svg or img), 2.25rem, with a drop shadow in brand 1.
6
+ .oc-brand-mark__text: Word mark and tagline stacked.
7
+ .oc-brand-mark__word: The name: --oc-font-display, 800, tight letter spacing.
8
+ .oc-brand-mark__accent: The part of the name in the gradient brand 1 → brand 2.
9
+ .oc-brand-mark__tagline: Mono, uppercase, wide letter spacing, muted.
10
+ .oc-brand-mark--sm / --lg: Smaller (footer) or larger (hero, ecosystem map). The sizes are tokens: --oc-brand-mark-icon, --oc-brand-mark-word, --oc-brand-mark-tagline.
11
+ -->
12
+ <a class="oc-brand-mark" href="#home" aria-label="Company 1, home">
13
+ <svg class="oc-brand-mark__icon" viewBox="0 0 40 40" aria-hidden="true"><defs><linearGradient id="oc-brand-grad" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="var(--oc-brand-1)"/><stop offset="1" stop-color="var(--oc-brand-2)"/></linearGradient></defs><rect width="40" height="40" rx="10" fill="url(#oc-brand-grad)"/><path d="M12 27V13h9a5 5 0 0 1 0 10h-5v4z" fill="var(--oc-background)"/></svg>
14
+ <span class="oc-brand-mark__text">
15
+ <span class="oc-brand-mark__word">Company<span class="oc-brand-mark__accent">1</span></span>
16
+ <span class="oc-brand-mark__tagline">Software &amp; IT</span>
17
+ </span>
18
+ </a>
19
+ <div style="display:flex;flex-wrap:wrap;align-items:center;gap:2.5rem">
20
+ <a class="oc-brand-mark oc-brand-mark--sm" href="#home" aria-label="Company 1, home">
21
+ <svg class="oc-brand-mark__icon" viewBox="0 0 40 40" aria-hidden="true"><defs><linearGradient id="oc-brand-grad-sm" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="var(--oc-brand-1)"/><stop offset="1" stop-color="var(--oc-brand-2)"/></linearGradient></defs><rect width="40" height="40" rx="10" fill="url(#oc-brand-grad-sm)"/><path d="M12 27V13h9a5 5 0 0 1 0 10h-5v4z" fill="var(--oc-background)"/></svg>
22
+ <span class="oc-brand-mark__text">
23
+ <span class="oc-brand-mark__word">Company<span class="oc-brand-mark__accent">1</span></span>
24
+ <span class="oc-brand-mark__tagline">Software &amp; IT</span>
25
+ </span>
26
+ </a>
27
+ <a class="oc-brand-mark" href="#home" aria-label="Company 1, home">
28
+ <svg class="oc-brand-mark__icon" viewBox="0 0 40 40" aria-hidden="true"><defs><linearGradient id="oc-brand-grad-md" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="var(--oc-brand-1)"/><stop offset="1" stop-color="var(--oc-brand-2)"/></linearGradient></defs><rect width="40" height="40" rx="10" fill="url(#oc-brand-grad-md)"/><path d="M12 27V13h9a5 5 0 0 1 0 10h-5v4z" fill="var(--oc-background)"/></svg>
29
+ <span class="oc-brand-mark__text">
30
+ <span class="oc-brand-mark__word">Company<span class="oc-brand-mark__accent">1</span></span>
31
+ <span class="oc-brand-mark__tagline">Software &amp; IT</span>
32
+ </span>
33
+ </a>
34
+ <a class="oc-brand-mark oc-brand-mark--lg" href="#home" aria-label="Company 1, home">
35
+ <svg class="oc-brand-mark__icon" viewBox="0 0 40 40" aria-hidden="true"><defs><linearGradient id="oc-brand-grad-lg" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="var(--oc-brand-1)"/><stop offset="1" stop-color="var(--oc-brand-2)"/></linearGradient></defs><rect width="40" height="40" rx="10" fill="url(#oc-brand-grad-lg)"/><path d="M12 27V13h9a5 5 0 0 1 0 10h-5v4z" fill="var(--oc-background)"/></svg>
36
+ <span class="oc-brand-mark__text">
37
+ <span class="oc-brand-mark__word">Company<span class="oc-brand-mark__accent">1</span></span>
38
+ <span class="oc-brand-mark__tagline">Software &amp; IT</span>
39
+ </span>
40
+ </a>
41
+ </div>
@@ -0,0 +1,52 @@
1
+ <!-- OmniaUI component: feature-card - Cards for services and features (icon tile, title, text) and cards that are links, in a grid whose column width you set. Needs @birdycom/omniaui CSS.
2
+ File: packages/ui/snippets/feature-card.html | Map: services -> link cards -> brand chips.
3
+ Class API (modifiers are extra classes / attributes on the same element):
4
+ .oc-feature-card: Card: tile, title, text; surface background, the border turns strong on hover.
5
+ .oc-feature-card__title / __text: Title (display font, 1.2rem) and muted text.
6
+ .oc-link-card: A card that is an <a>: __title, __text, __more (arrow moves on hover); the border takes the accent color on hover (.oc-accent--violet …).
7
+ .oc-card-grid: Auto-fill grid; --oc-card-min is the minimum column width (17rem), --oc-card-gap the gap.
8
+ .oc-card-grid--wide: Wide cards (projects): from 32rem, three in a row from 1280px.
9
+ .oc-tag-list--neutral / --lg: Chips without the accent color / larger chips in the display font (a strip of brand names).
10
+ -->
11
+ <div class="oc-card-grid" style="--oc-card-min: 15.5rem; inline-size:100%">
12
+ <article class="oc-feature-card">
13
+ <span class="oc-icon-tile oc-icon-tile--sm" aria-hidden="true"><iconify-icon icon="mdi:application-outline"></iconify-icon></span>
14
+ <h3 class="oc-feature-card__title">Business apps</h3>
15
+ <p class="oc-feature-card__text">Software built around how your company works.</p>
16
+ </article>
17
+ <article class="oc-feature-card">
18
+ <span class="oc-icon-tile oc-icon-tile--sm" aria-hidden="true"><iconify-icon icon="mdi:web"></iconify-icon></span>
19
+ <h3 class="oc-feature-card__title">Websites</h3>
20
+ <p class="oc-feature-card__text">Fast sites you can edit yourself.</p>
21
+ </article>
22
+ <article class="oc-feature-card">
23
+ <span class="oc-icon-tile oc-icon-tile--sm" aria-hidden="true"><iconify-icon icon="mdi:laptop"></iconify-icon></span>
24
+ <h3 class="oc-feature-card__title">Hardware</h3>
25
+ <p class="oc-feature-card__text">Computers, screens and networks, installed on site.</p>
26
+ </article>
27
+ <article class="oc-feature-card">
28
+ <span class="oc-icon-tile oc-icon-tile--sm" aria-hidden="true"><iconify-icon icon="mdi:headset"></iconify-icon></span>
29
+ <h3 class="oc-feature-card__title">Support</h3>
30
+ <p class="oc-feature-card__text">One point of contact for every question.</p>
31
+ </article>
32
+ </div>
33
+ <div class="oc-card-grid" style="--oc-card-min: 16rem; inline-size:100%">
34
+ <a class="oc-link-card oc-accent--violet" href="#agent-1">
35
+ <span class="oc-icon-tile oc-icon-tile--sm oc-accent--violet" aria-hidden="true"><iconify-icon icon="mdi:robot-outline"></iconify-icon></span>
36
+ <span class="oc-link-card__title">Agent 1</span>
37
+ <span class="oc-link-card__text">Answers questions about your own documents.</span>
38
+ <span class="oc-link-card__more">Read more</span>
39
+ </a>
40
+ <a class="oc-link-card oc-accent--violet" href="#agent-2">
41
+ <span class="oc-icon-tile oc-icon-tile--sm oc-accent--violet" aria-hidden="true"><iconify-icon icon="mdi:file-search-outline"></iconify-icon></span>
42
+ <span class="oc-link-card__title">Agent 2</span>
43
+ <span class="oc-link-card__text">Reads incoming invoices and fills in the booking.</span>
44
+ <span class="oc-link-card__more">Read more</span>
45
+ </a>
46
+ </div>
47
+ <div style="display:grid;gap:1rem">
48
+ <ul class="oc-tag-list oc-tag-list--neutral oc-tag-list--lg" aria-label="Brands we work with">
49
+ <li>Brand 1</li><li>Brand 2</li><li>Brand 3</li><li>Brand 4</li><li>Brand 5</li>
50
+ </ul>
51
+ <p class="oc-note" style="margin:0">Other brands on request.</p>
52
+ </div>