openvibers/openvibe.shared Commits
4.100.0: a card's day as its cover; the aura's mark leaves the heading alone on a phone (#260)
Changes
@@@ -4,6 +4,16 @@ All notable changes to `openvibe-shared`. Versions follow [semver](https://semvebreaking change to any exported module, browser global or served file name is a new major.A release is the git tag `vX.Y.Z`; consumers pin the tag's tarball (see README).++## 4.100.0 — 2026-11-02++++From a phone sweep of every site's home (11-02):++- **A card's day as its cover.** `kit.card({ date })`: a thing without a picture that happens on a day (a drop, an++ event, a deadline) shows the weekday and month with the day large, instead of the same calendar icon on every card.++ The day stays clear of the corner's price. Shoes' "Dropping this week" was a row of identical calendar icons.++- **The aura's mark leaves the heading alone on a phone.** On a phone the aura hero (`sc-has-vis` without an orbit) is a++ watermark behind the heading, and its mark landed at the end of a long heading: AI's read "one API.V", Download's++ and Watch's the same. The glow stays; the mark goes there (the navbar shows it). Computers keep it in its column.++## 4.99.1 — 2026-11-02- **The conversation's head follows the navbar on a phone.** The navbar slides away while you read down. The thread's@@@ -2,7 +2,7 @@> Versioned UI, the OpenVibe Frame (the navbar, footer and "shipped" views every site sits in), SEO, legal and release-client packages every OpenVibe site renders.--**Status:** alpha, v4.99.1 (every release is a tag; see [CHANGELOG.md](CHANGELOG.md)). Every deployed++**Status:** alpha, v4.100.0 (every release is a tag; see [CHANGELOG.md](CHANGELOG.md)). Every deployedconsumer installs a tagged release and none keeps a vendored copy([docs/migration-plan.md](docs/migration-plan.md) is done); each repository's `package.json` namesthe tag it pins.@@@ -4,7 +4,7 @@"code": true,"wave": "W2","package": "openvibe-shared",-- "version": "4.99.1",++ "version": "4.100.0","plan": "OpenVibe End-to-End Realignment & Implementation Plan, revision 3 (20 Sep 2026)","deployed": false,"deployment": "none of its own: a package released as git tags; OpenVibe.Network serves /shared/* from its pinned tag, and a site can serve its own pinned copy (openvibe-shared/serve)",@@@ -199,7 +199,7 @@ kit.badge(text, 'accent' | 'ok' | 'warn' | 'bad' | 'live') · kit.dot(n) · kit.### Cards and rails (4.28.0)```js--kit.card({ href, title, type, ratio, image, alt, video, badge, live, corner, peek, who, whoHtml, avatar, sub, preview,++kit.card({ href, title, type, ratio, image, alt, video, badge, live, corner, peek, who, whoHtml, avatar, sub, preview, date,count: [1832, 'views'], when, level, save: { href }, remind: { href }, share, more: [items] })kit.rail({ id, title, more: { href, label }, items: [kit.card(…), …], size: 's' | 'm' | 'l', wrap }))```@@@ -208,7 +208,7 @@ One card for every kind of thing (plan 2026-10-28 §3.4):| Part | What it does || --- | --- |--| Media | In the type's shape (`kit.CARD_RATIO`): 16:9 streams, clips, runs, maps, cams, events; 1:1 memes, shoes, listings, deals; 4:3 reports, themes, games; 2:1 domains. `ratio` overrides it. Without a picture, the type's icon on a wash of the room's accent |++| Media | In the type's shape (`kit.CARD_RATIO`): 16:9 streams, clips, runs, maps, cams, events; 1:1 memes, shoes, listings, deals; 4:3 reports, themes, games; 2:1 domains. `ratio` overrides it. Without a picture, the type's icon on a wash of the room's accent; a person's face when `avatar` is given; the first lines when `preview` is; and the day itself (weekday and month, the day large) when `date` is (a drop, an event, a deadline) || Badge | Top left, one word: NEW, −24 %, WR. `live: true` draws LIVE with a pulse || Corner | Bottom right: a length or a price || Peek | One line over the media on hover or focus |@@@ -411,13 +411,25 @@* name as markup the caller escaped (a name with its decorations); who stays the plain name for the letters* - preview: for a thing without a picture whose text is the point (a code paste, a note), its first lines drawn in* the media instead of the type's icon++ * - date: for a thing without a picture that happens on a day (a drop, an event, a deadline), the day itself drawn in++ * the media (weekday and month, the day large; UTC) instead of a calendar icon repeated on every card* - ⋯ with Save and Remind when given ({ href } or { action, hidden }), Share (always: the system share sheet, else* the link copied) and any `more` items (kit.more's)* The card is one link, the title's, stretched over it; ⋯ sits above it. level: the title's heading level (3).*/++ // A day as a card's cover (card({ date })): the weekday and month on a line, the day large under it (clear of the++ // corner's price). UTC, as the sites' dates are.++ function dateCover(value) {++ if (value == null || value === '') return '';++ const t = value instanceof Date ? value.getTime() : typeof value === 'number' ? value : Date.parse(String(value));++ if (!Number.isFinite(t)) return '';++ const d = new Date(t);++ return `<span class="ovk-card-noimg ovk-card-noimg--date" aria-hidden="true"><span class="ovk-card-date-m"><span class="ovk-card-date-w">${WEEKDAYS[d.getUTCDay()]}</span> ${MONTHS[d.getUTCMonth()]}</span>`++ + `<b class="ovk-card-date-d">${d.getUTCDate()}</b></span>`;++ }function card({ href = '#', title = '', type = '', ratio = '', image = '', alt = '', video = '', badge: bd = '', live = false, corner = '',peek = '', who = '', whoHtml = null, avatar = '', count = null, when: at = null, level = 3, save = null, remind = null, share = true,-- more: extra = null, sub = '', preview = '' } = {}) {++ more: extra = null, sub = '', preview = '', date = null } = {}) {const ar = /^\d{1,2}\/\d{1,2}$/.test(String(ratio)) ? ratio : CARD_RATIO[type] || '16/9';const link = url(href);const first = String(preview || '').split('\n').slice(0, 9).join('\n').slice(0, 480).trimEnd();@@@ -427,7 +439,7 @@// No picture but a person (2026-11-01): their face, large, over a soft blur of it, where a lone type icon// in an empty box read as a missing image (Report's episodes, Tours' recordings).: media(avatar) ? `<span class="ovk-card-noimg ovk-card-noimg--who" aria-hidden="true"><img class="ovk-card-noimg-bg" src="${esc(mediaW(avatar, 160))}" alt="" loading="lazy" decoding="async"><img class="ovk-card-noimg-face" src="${esc(mediaW(avatar, 160))}" alt="" loading="lazy" decoding="async"></span>`-- : `<span class="ovk-card-noimg">${icon(CARD_ICON[type] || 'image')}</span>`;++ : dateCover(date) || `<span class="ovk-card-noimg">${icon(CARD_ICON[type] || 'image')}</span>`;const loop = media(video) ? `<video class="ovk-card-video" data-src="${esc(video)}" muted loop playsinline preload="none" aria-hidden="true" tabindex="-1"></video>` : '';const tag = live ? '<span class="ovk-card-badge is-live">LIVE</span>' : bd ? `<span class="ovk-card-badge">${esc(bd)}</span>` : '';const n = Array.isArray(count) ? Number(count[0]) : Number(count);@@@ -1,6 +1,6 @@{"name": "openvibe-shared",-- "version": "4.99.1",++ "version": "4.100.0","description": "Shared navbar, footer, themes, icons, legal pages, SEO, auth and analytics for every OpenVibe site","main": "index.js","bin": {@@@ -100,6 +100,11 @@.ovk-card-noimg .ovk-i { width: 30%; max-width: 60px; height: auto; aspect-ratio: 1; stroke-width: 1.5; }/* No picture but a person (2026-11-01): their face, large, over a soft blur of it, not a lone icon in an empty box. */.ovk-card-noimg--who { overflow: hidden; }++/* A day as the cover (kit.card({ date })): the month in the accent, the day large, the weekday quiet. */++.ovk-card-noimg--date { container-type: inline-size; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 0; padding-bottom: 8%; line-height: 1; font-variant-numeric: tabular-nums; }++.ovk-card-date-m { font-size: clamp(11px, 6cqi, 15px); font-weight: 750; letter-spacing: .12em; text-transform: uppercase; }++.ovk-card-date-d { margin: 4px 0 0; font-size: clamp(30px, 23cqi, 64px); font-weight: 800; letter-spacing: -.03em; color: var(--ovk-text, var(--text-primary, #e6edf7)); }++.ovk-card-date-w { font-weight: 650; color: var(--ovk-muted, var(--text-muted, #8b97ab)); }.ovk-card-noimg--who .ovk-card-noimg-bg { position: absolute; inset: -15%; width: 130%; height: 130%; object-fit: cover; filter: blur(24px) saturate(1.15); opacity: .5; }.ovk-card-noimg--who .ovk-card-noimg-face { position: relative; width: 28%; max-width: 88px; aspect-ratio: 1; border-radius: 50%; object-fit: cover; box-shadow: 0 0 0 3px color-mix(in srgb, var(--ovk-panel, #111a2c) 70%, transparent), 0 10px 28px rgb(0 0 0 / .28); }.ovk-card-badge { position: absolute; top: 8px; left: 8px; z-index: 1; display: inline-flex; align-items: center; gap: 5px; padding: 2px 8px; border-radius: 6px; background: rgba(0, 0, 0, .74); color: #fff; font: 800 11.5px/1.5 var(--ovk-font); letter-spacing: .04em; }@@@ -28,7 +28,7 @@ html[data-ov-motion=reduced] .sc-ring,html[data-ov-motion=reduced] .sc-aura-glow@media (max-width:860px){.sc-hero.sc-has-vis{display:block}.sc-hero.sc-has-vis .sc-hero-vis{position:absolute;z-index:-1;top:-26px;right:0;width:180px;opacity:.36;pointer-events:none}--.sc-hero.sc-has-vis .sc-aura .sc-vis-mark{width:46px!important;height:46px!important}++.sc-hero.sc-has-vis .sc-aura .sc-vis-mark{display:none!important}.sc-hero.sc-has-orbit{display:flex;flex-direction:column}.sc-hero.sc-has-orbit .sc-hero-vis{order:-1;position:static;width:auto;opacity:1;pointer-events:auto;margin:0 0 6px}.sc-hero.sc-has-orbit .sc-vis{width:min(70vw,250px)}@@@ -456,7 +456,7 @@ html[data-ov-motion=reduced] .sc-ring,html[data-ov-motion=reduced] .sc-aura-glow@media (max-width:860px){.sc-hero.sc-has-vis{display:block}.sc-hero.sc-has-vis .sc-hero-vis{position:absolute;z-index:-1;top:-26px;right:0;width:180px;opacity:.36;pointer-events:none}--.sc-hero.sc-has-vis .sc-aura .sc-vis-mark{width:46px!important;height:46px!important}++.sc-hero.sc-has-vis .sc-aura .sc-vis-mark{display:none!important}.sc-hero.sc-has-orbit{display:flex;flex-direction:column}.sc-hero.sc-has-orbit .sc-hero-vis{order:-1;position:static;width:auto;opacity:1;pointer-events:auto;margin:0 0 6px}.sc-hero.sc-has-orbit .sc-vis{width:min(70vw,250px)}@@@ -286,4 +286,12 @@ const kit = require('../kit');assert.ok(/ov:boost:before', \(\) => Array\.prototype\.forEach\.call\(document\.querySelectorAll\('\.ovk-sidebar\.is-open'\)/.test(web), 'and closed by a page move');}++// card({ date }) (4.100.0): a thing without a picture that happens on a day shows the day, not a repeated calendar icon.++{++ const dated = kit.card({ href: '/drops#d1', title: 'Nike Air Max Dolce', type: 'event', corner: '$170.00', date: '2026-11-04T15:00:00Z' });++ assert.ok(dated.includes('<span class="ovk-card-noimg ovk-card-noimg--date" aria-hidden="true"><span class="ovk-card-date-m"><span class="ovk-card-date-w">Wed</span> Nov</span><b class="ovk-card-date-d">4</b></span>'), 'the day as the cover, in UTC');++ assert.ok(!kit.card({ title: 'x', type: 'event', date: 'not a date' }).includes('--date') && !kit.card({ title: 'x', type: 'event' }).includes('--date'), 'no date, or not a date: the type icon as before');++ assert.ok(!kit.card({ title: 'x', type: 'event', image: 'https://example.com/a.jpg', date: '2026-11-04' }).includes('--date'), 'a picture wins over the date');++}++console.log('kit: builders, escaping, tabs, rows, settings, find/dock, icons, time and Kit 2 (steps, table, chart, rank, meter, drop, receipt, page), the tool frame and the sidebar ok');
@@ -4,6 +4,16 @@ All notable changes to `openvibe-shared`. Versions follow [semver](https://semve
@@ -4,6 +4,16 @@ All notable changes to `openvibe-shared`. Versions follow [semver](https://semve
breaking change to any exported module, browser global or served file name is a new major.
breaking change to any exported module, browser global or served file name is a new major.
A release is the git tag `vX.Y.Z`; consumers pin the tag's tarball (see README).
A release is the git tag `vX.Y.Z`; consumers pin the tag's tarball (see README).
+## 4.100.0 — 2026-11-02
+
+From a phone sweep of every site's home (11-02):
+- **A card's day as its cover.** `kit.card({ date })`: a thing without a picture that happens on a day (a drop, an
+ event, a deadline) shows the weekday and month with the day large, instead of the same calendar icon on every card.
+ The day stays clear of the corner's price. Shoes' "Dropping this week" was a row of identical calendar icons.
+- **The aura's mark leaves the heading alone on a phone.** On a phone the aura hero (`sc-has-vis` without an orbit) is a
+ watermark behind the heading, and its mark landed at the end of a long heading: AI's read "one API.V", Download's
+ and Watch's the same. The glow stays; the mark goes there (the navbar shows it). Computers keep it in its column.
+
## 4.99.1 — 2026-11-02
## 4.99.1 — 2026-11-02
- **The conversation's head follows the navbar on a phone.** The navbar slides away while you read down. The thread's
- **The conversation's head follows the navbar on a phone.** The navbar slides away while you read down. The thread's
@@ -2,7 +2,7 @@
@@ -2,7 +2,7 @@
> Versioned UI, the OpenVibe Frame (the navbar, footer and "shipped" views every site sits in), SEO, legal and release-client packages every OpenVibe site renders.
> Versioned UI, the OpenVibe Frame (the navbar, footer and "shipped" views every site sits in), SEO, legal and release-client packages every OpenVibe site renders.
-**Status:** alpha, v4.99.1 (every release is a tag; see [CHANGELOG.md](CHANGELOG.md)). Every deployed
+**Status:** alpha, v4.100.0 (every release is a tag; see [CHANGELOG.md](CHANGELOG.md)). Every deployed
consumer installs a tagged release and none keeps a vendored copy
consumer installs a tagged release and none keeps a vendored copy
([docs/migration-plan.md](docs/migration-plan.md) is done); each repository's `package.json` names
([docs/migration-plan.md](docs/migration-plan.md) is done); each repository's `package.json` names
the tag it pins.
the tag it pins.
@@ -4,7 +4,7 @@
@@ -4,7 +4,7 @@
"code": true,
"code": true,
"wave": "W2",
"wave": "W2",
"package": "openvibe-shared",
"package": "openvibe-shared",
- "version": "4.99.1",
+ "version": "4.100.0",
"plan": "OpenVibe End-to-End Realignment & Implementation Plan, revision 3 (20 Sep 2026)",
"plan": "OpenVibe End-to-End Realignment & Implementation Plan, revision 3 (20 Sep 2026)",
"deployed": false,
"deployed": false,
"deployment": "none of its own: a package released as git tags; OpenVibe.Network serves /shared/* from its pinned tag, and a site can serve its own pinned copy (openvibe-shared/serve)",
"deployment": "none of its own: a package released as git tags; OpenVibe.Network serves /shared/* from its pinned tag, and a site can serve its own pinned copy (openvibe-shared/serve)",
@@ -199,7 +199,7 @@ kit.badge(text, 'accent' | 'ok' | 'warn' | 'bad' | 'live') · kit.dot(n) · kit.
@@ -199,7 +199,7 @@ kit.badge(text, 'accent' | 'ok' | 'warn' | 'bad' | 'live') · kit.dot(n) · kit.
### Cards and rails (4.28.0)
### Cards and rails (4.28.0)
```js
```js
-kit.card({ href, title, type, ratio, image, alt, video, badge, live, corner, peek, who, whoHtml, avatar, sub, preview,
+kit.card({ href, title, type, ratio, image, alt, video, badge, live, corner, peek, who, whoHtml, avatar, sub, preview, date,
count: [1832, 'views'], when, level, save: { href }, remind: { href }, share, more: [items] })
count: [1832, 'views'], when, level, save: { href }, remind: { href }, share, more: [items] })
kit.rail({ id, title, more: { href, label }, items: [kit.card(…), …], size: 's' | 'm' | 'l', wrap }))
kit.rail({ id, title, more: { href, label }, items: [kit.card(…), …], size: 's' | 'm' | 'l', wrap }))
```
```
@@ -208,7 +208,7 @@ One card for every kind of thing (plan 2026-10-28 §3.4):
@@ -208,7 +208,7 @@ One card for every kind of thing (plan 2026-10-28 §3.4):
| Part | What it does |
| Part | What it does |
| --- | --- |
| --- | --- |
-| Media | In the type's shape (`kit.CARD_RATIO`): 16:9 streams, clips, runs, maps, cams, events; 1:1 memes, shoes, listings, deals; 4:3 reports, themes, games; 2:1 domains. `ratio` overrides it. Without a picture, the type's icon on a wash of the room's accent |
+| Media | In the type's shape (`kit.CARD_RATIO`): 16:9 streams, clips, runs, maps, cams, events; 1:1 memes, shoes, listings, deals; 4:3 reports, themes, games; 2:1 domains. `ratio` overrides it. Without a picture, the type's icon on a wash of the room's accent; a person's face when `avatar` is given; the first lines when `preview` is; and the day itself (weekday and month, the day large) when `date` is (a drop, an event, a deadline) |
| Badge | Top left, one word: NEW, −24 %, WR. `live: true` draws LIVE with a pulse |
| Badge | Top left, one word: NEW, −24 %, WR. `live: true` draws LIVE with a pulse |
| Corner | Bottom right: a length or a price |
| Corner | Bottom right: a length or a price |
| Peek | One line over the media on hover or focus |
| Peek | One line over the media on hover or focus |
@@ -411,13 +411,25 @@
@@ -411,13 +411,25 @@
* name as markup the caller escaped (a name with its decorations); who stays the plain name for the letters
* name as markup the caller escaped (a name with its decorations); who stays the plain name for the letters
* - preview: for a thing without a picture whose text is the point (a code paste, a note), its first lines drawn in
* - preview: for a thing without a picture whose text is the point (a code paste, a note), its first lines drawn in
* the media instead of the type's icon
* the media instead of the type's icon
+ * - date: for a thing without a picture that happens on a day (a drop, an event, a deadline), the day itself drawn in
+ * the media (weekday and month, the day large; UTC) instead of a calendar icon repeated on every card
* - ⋯ with Save and Remind when given ({ href } or { action, hidden }), Share (always: the system share sheet, else
* - ⋯ with Save and Remind when given ({ href } or { action, hidden }), Share (always: the system share sheet, else
* the link copied) and any `more` items (kit.more's)
* the link copied) and any `more` items (kit.more's)
* The card is one link, the title's, stretched over it; ⋯ sits above it. level: the title's heading level (3).
* The card is one link, the title's, stretched over it; ⋯ sits above it. level: the title's heading level (3).
*/
*/
+ // A day as a card's cover (card({ date })): the weekday and month on a line, the day large under it (clear of the
+ // corner's price). UTC, as the sites' dates are.
+ function dateCover(value) {
+ if (value == null || value === '') return '';
+ const t = value instanceof Date ? value.getTime() : typeof value === 'number' ? value : Date.parse(String(value));
+ if (!Number.isFinite(t)) return '';
+ const d = new Date(t);
+ return `<span class="ovk-card-noimg ovk-card-noimg--date" aria-hidden="true"><span class="ovk-card-date-m"><span class="ovk-card-date-w">${WEEKDAYS[d.getUTCDay()]}</span> ${MONTHS[d.getUTCMonth()]}</span>`
+ + `<b class="ovk-card-date-d">${d.getUTCDate()}</b></span>`;
+ }
function card({ href = '#', title = '', type = '', ratio = '', image = '', alt = '', video = '', badge: bd = '', live = false, corner = '',
function card({ href = '#', title = '', type = '', ratio = '', image = '', alt = '', video = '', badge: bd = '', live = false, corner = '',
peek = '', who = '', whoHtml = null, avatar = '', count = null, when: at = null, level = 3, save = null, remind = null, share = true,
peek = '', who = '', whoHtml = null, avatar = '', count = null, when: at = null, level = 3, save = null, remind = null, share = true,
- more: extra = null, sub = '', preview = '' } = {}) {
+ more: extra = null, sub = '', preview = '', date = null } = {}) {
const ar = /^\d{1,2}\/\d{1,2}$/.test(String(ratio)) ? ratio : CARD_RATIO[type] || '16/9';
const ar = /^\d{1,2}\/\d{1,2}$/.test(String(ratio)) ? ratio : CARD_RATIO[type] || '16/9';
const link = url(href);
const link = url(href);
const first = String(preview || '').split('\n').slice(0, 9).join('\n').slice(0, 480).trimEnd();
const first = String(preview || '').split('\n').slice(0, 9).join('\n').slice(0, 480).trimEnd();
@@ -427,7 +439,7 @@
@@ -427,7 +439,7 @@
// No picture but a person (2026-11-01): their face, large, over a soft blur of it, where a lone type icon
// No picture but a person (2026-11-01): their face, large, over a soft blur of it, where a lone type icon
// in an empty box read as a missing image (Report's episodes, Tours' recordings).
// in an empty box read as a missing image (Report's episodes, Tours' recordings).
: media(avatar) ? `<span class="ovk-card-noimg ovk-card-noimg--who" aria-hidden="true"><img class="ovk-card-noimg-bg" src="${esc(mediaW(avatar, 160))}" alt="" loading="lazy" decoding="async"><img class="ovk-card-noimg-face" src="${esc(mediaW(avatar, 160))}" alt="" loading="lazy" decoding="async"></span>`
: media(avatar) ? `<span class="ovk-card-noimg ovk-card-noimg--who" aria-hidden="true"><img class="ovk-card-noimg-bg" src="${esc(mediaW(avatar, 160))}" alt="" loading="lazy" decoding="async"><img class="ovk-card-noimg-face" src="${esc(mediaW(avatar, 160))}" alt="" loading="lazy" decoding="async"></span>`
- : `<span class="ovk-card-noimg">${icon(CARD_ICON[type] || 'image')}</span>`;
+ : dateCover(date) || `<span class="ovk-card-noimg">${icon(CARD_ICON[type] || 'image')}</span>`;
const loop = media(video) ? `<video class="ovk-card-video" data-src="${esc(video)}" muted loop playsinline preload="none" aria-hidden="true" tabindex="-1"></video>` : '';
const loop = media(video) ? `<video class="ovk-card-video" data-src="${esc(video)}" muted loop playsinline preload="none" aria-hidden="true" tabindex="-1"></video>` : '';
const tag = live ? '<span class="ovk-card-badge is-live">LIVE</span>' : bd ? `<span class="ovk-card-badge">${esc(bd)}</span>` : '';
const tag = live ? '<span class="ovk-card-badge is-live">LIVE</span>' : bd ? `<span class="ovk-card-badge">${esc(bd)}</span>` : '';
const n = Array.isArray(count) ? Number(count[0]) : Number(count);
const n = Array.isArray(count) ? Number(count[0]) : Number(count);
@@ -1,6 +1,6 @@
@@ -1,6 +1,6 @@
{
{
"name": "openvibe-shared",
"name": "openvibe-shared",
- "version": "4.99.1",
+ "version": "4.100.0",
"description": "Shared navbar, footer, themes, icons, legal pages, SEO, auth and analytics for every OpenVibe site",
"description": "Shared navbar, footer, themes, icons, legal pages, SEO, auth and analytics for every OpenVibe site",
"main": "index.js",
"main": "index.js",
"bin": {
"bin": {
@@ -100,6 +100,11 @@
@@ -100,6 +100,11 @@
.ovk-card-noimg .ovk-i { width: 30%; max-width: 60px; height: auto; aspect-ratio: 1; stroke-width: 1.5; }
.ovk-card-noimg .ovk-i { width: 30%; max-width: 60px; height: auto; aspect-ratio: 1; stroke-width: 1.5; }
/* No picture but a person (2026-11-01): their face, large, over a soft blur of it, not a lone icon in an empty box. */
/* No picture but a person (2026-11-01): their face, large, over a soft blur of it, not a lone icon in an empty box. */
.ovk-card-noimg--who { overflow: hidden; }
.ovk-card-noimg--who { overflow: hidden; }
+/* A day as the cover (kit.card({ date })): the month in the accent, the day large, the weekday quiet. */
+.ovk-card-noimg--date { container-type: inline-size; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 0; padding-bottom: 8%; line-height: 1; font-variant-numeric: tabular-nums; }
+.ovk-card-date-m { font-size: clamp(11px, 6cqi, 15px); font-weight: 750; letter-spacing: .12em; text-transform: uppercase; }
+.ovk-card-date-d { margin: 4px 0 0; font-size: clamp(30px, 23cqi, 64px); font-weight: 800; letter-spacing: -.03em; color: var(--ovk-text, var(--text-primary, #e6edf7)); }
+.ovk-card-date-w { font-weight: 650; color: var(--ovk-muted, var(--text-muted, #8b97ab)); }
.ovk-card-noimg--who .ovk-card-noimg-bg { position: absolute; inset: -15%; width: 130%; height: 130%; object-fit: cover; filter: blur(24px) saturate(1.15); opacity: .5; }
.ovk-card-noimg--who .ovk-card-noimg-bg { position: absolute; inset: -15%; width: 130%; height: 130%; object-fit: cover; filter: blur(24px) saturate(1.15); opacity: .5; }
.ovk-card-noimg--who .ovk-card-noimg-face { position: relative; width: 28%; max-width: 88px; aspect-ratio: 1; border-radius: 50%; object-fit: cover; box-shadow: 0 0 0 3px color-mix(in srgb, var(--ovk-panel, #111a2c) 70%, transparent), 0 10px 28px rgb(0 0 0 / .28); }
.ovk-card-noimg--who .ovk-card-noimg-face { position: relative; width: 28%; max-width: 88px; aspect-ratio: 1; border-radius: 50%; object-fit: cover; box-shadow: 0 0 0 3px color-mix(in srgb, var(--ovk-panel, #111a2c) 70%, transparent), 0 10px 28px rgb(0 0 0 / .28); }
.ovk-card-badge { position: absolute; top: 8px; left: 8px; z-index: 1; display: inline-flex; align-items: center; gap: 5px; padding: 2px 8px; border-radius: 6px; background: rgba(0, 0, 0, .74); color: #fff; font: 800 11.5px/1.5 var(--ovk-font); letter-spacing: .04em; }
.ovk-card-badge { position: absolute; top: 8px; left: 8px; z-index: 1; display: inline-flex; align-items: center; gap: 5px; padding: 2px 8px; border-radius: 6px; background: rgba(0, 0, 0, .74); color: #fff; font: 800 11.5px/1.5 var(--ovk-font); letter-spacing: .04em; }
@@ -28,7 +28,7 @@ html[data-ov-motion=reduced] .sc-ring,html[data-ov-motion=reduced] .sc-aura-glow
@@ -28,7 +28,7 @@ html[data-ov-motion=reduced] .sc-ring,html[data-ov-motion=reduced] .sc-aura-glow
@media (max-width:860px){
@media (max-width:860px){
.sc-hero.sc-has-vis{display:block}
.sc-hero.sc-has-vis{display:block}
.sc-hero.sc-has-vis .sc-hero-vis{position:absolute;z-index:-1;top:-26px;right:0;width:180px;opacity:.36;pointer-events:none}
.sc-hero.sc-has-vis .sc-hero-vis{position:absolute;z-index:-1;top:-26px;right:0;width:180px;opacity:.36;pointer-events:none}
-.sc-hero.sc-has-vis .sc-aura .sc-vis-mark{width:46px!important;height:46px!important}
+.sc-hero.sc-has-vis .sc-aura .sc-vis-mark{display:none!important}
.sc-hero.sc-has-orbit{display:flex;flex-direction:column}
.sc-hero.sc-has-orbit{display:flex;flex-direction:column}
.sc-hero.sc-has-orbit .sc-hero-vis{order:-1;position:static;width:auto;opacity:1;pointer-events:auto;margin:0 0 6px}
.sc-hero.sc-has-orbit .sc-hero-vis{order:-1;position:static;width:auto;opacity:1;pointer-events:auto;margin:0 0 6px}
.sc-hero.sc-has-orbit .sc-vis{width:min(70vw,250px)}
.sc-hero.sc-has-orbit .sc-vis{width:min(70vw,250px)}
@@ -456,7 +456,7 @@ html[data-ov-motion=reduced] .sc-ring,html[data-ov-motion=reduced] .sc-aura-glow
@@ -456,7 +456,7 @@ html[data-ov-motion=reduced] .sc-ring,html[data-ov-motion=reduced] .sc-aura-glow
@media (max-width:860px){
@media (max-width:860px){
.sc-hero.sc-has-vis{display:block}
.sc-hero.sc-has-vis{display:block}
.sc-hero.sc-has-vis .sc-hero-vis{position:absolute;z-index:-1;top:-26px;right:0;width:180px;opacity:.36;pointer-events:none}
.sc-hero.sc-has-vis .sc-hero-vis{position:absolute;z-index:-1;top:-26px;right:0;width:180px;opacity:.36;pointer-events:none}
-.sc-hero.sc-has-vis .sc-aura .sc-vis-mark{width:46px!important;height:46px!important}
+.sc-hero.sc-has-vis .sc-aura .sc-vis-mark{display:none!important}
.sc-hero.sc-has-orbit{display:flex;flex-direction:column}
.sc-hero.sc-has-orbit{display:flex;flex-direction:column}
.sc-hero.sc-has-orbit .sc-hero-vis{order:-1;position:static;width:auto;opacity:1;pointer-events:auto;margin:0 0 6px}
.sc-hero.sc-has-orbit .sc-hero-vis{order:-1;position:static;width:auto;opacity:1;pointer-events:auto;margin:0 0 6px}
.sc-hero.sc-has-orbit .sc-vis{width:min(70vw,250px)}
.sc-hero.sc-has-orbit .sc-vis{width:min(70vw,250px)}
@@ -286,4 +286,12 @@ const kit = require('../kit');
@@ -286,4 +286,12 @@ const kit = require('../kit');
assert.ok(/ov:boost:before', \(\) => Array\.prototype\.forEach\.call\(document\.querySelectorAll\('\.ovk-sidebar\.is-open'\)/.test(web), 'and closed by a page move');
assert.ok(/ov:boost:before', \(\) => Array\.prototype\.forEach\.call\(document\.querySelectorAll\('\.ovk-sidebar\.is-open'\)/.test(web), 'and closed by a page move');
}
}
+// card({ date }) (4.100.0): a thing without a picture that happens on a day shows the day, not a repeated calendar icon.
+{
+ const dated = kit.card({ href: '/drops#d1', title: 'Nike Air Max Dolce', type: 'event', corner: '$170.00', date: '2026-11-04T15:00:00Z' });
+ assert.ok(dated.includes('<span class="ovk-card-noimg ovk-card-noimg--date" aria-hidden="true"><span class="ovk-card-date-m"><span class="ovk-card-date-w">Wed</span> Nov</span><b class="ovk-card-date-d">4</b></span>'), 'the day as the cover, in UTC');
+ assert.ok(!kit.card({ title: 'x', type: 'event', date: 'not a date' }).includes('--date') && !kit.card({ title: 'x', type: 'event' }).includes('--date'), 'no date, or not a date: the type icon as before');
+ assert.ok(!kit.card({ title: 'x', type: 'event', image: 'https://example.com/a.jpg', date: '2026-11-04' }).includes('--date'), 'a picture wins over the date');
+}
+
console.log('kit: builders, escaping, tabs, rows, settings, find/dock, icons, time and Kit 2 (steps, table, chart, rank, meter, drop, receipt, page), the tool frame and the sidebar ok');
console.log('kit: builders, escaping, tabs, rows, settings, find/dock, icons, time and Kit 2 (steps, table, chart, rank, meter, drop, receipt, page), the tool frame and the sidebar ok');