Add Use Cases Showcase block with animated circles and descriptions

This commit is contained in:
Matt Batchelder
2026-02-21 10:46:40 -05:00
parent c42c7d7dbc
commit c5d5ad33e5
4 changed files with 591 additions and 1 deletions

View File

@@ -3874,3 +3874,404 @@ p:last-child { margin-bottom: 0; }
transform: rotate(-12deg) translateY(0) !important;
}
}
/* ═══════════════════════════════════════════════════════════════════════
USE CASES SHOWCASE (uc-*)
Four animated circles in a rounded-border track beneath the hero.
═══════════════════════════════════════════════════════════════════════ */
.uc-track {
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: 1.5rem;
border: 1px solid var(--color-border);
border-radius: var(--radius-xl, 1.25rem);
padding: 2.5rem 3rem;
background: var(--color-bg-alt);
}
.uc-item {
display: flex;
flex-direction: column;
align-items: center;
flex: 1;
}
.uc-circle {
width: 158px;
height: 158px;
border-radius: 50%;
border: 1.5px solid var(--color-border);
overflow: hidden;
position: relative;
background: var(--color-bg);
flex-shrink: 0;
}
.uc-item-body {
margin-top: 1.1rem;
text-align: center;
}
.uc-item-title {
font-size: .875rem;
font-weight: 600;
color: var(--color-heading);
}
.uc-item-desc {
font-size: .75rem;
color: var(--color-muted);
margin-top: .3rem;
line-height: 1.45;
max-width: 160px;
}
/* ── Shared inner wrapper ─────────────────────────────────────────────── */
.uc-inner {
position: absolute;
inset: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 18px;
box-sizing: border-box;
gap: 6px;
}
/* ══════════════════════════════════
1 — MENU BOARD
══════════════════════════════════ */
.uc-inner--menu { gap: 0; padding: 14px 18px; }
.uc-menu-header {
width: 100%;
height: 16px;
border-radius: 3px;
background: var(--color-primary);
margin-bottom: 10px;
flex-shrink: 0;
}
.uc-menu-row {
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
padding: 5px 4px;
border-radius: 3px;
gap: 6px;
flex-shrink: 0;
}
.uc-menu-row + .uc-menu-row { margin-top: 4px; }
.uc-menu-divider {
width: 100%;
height: 1px;
background: var(--color-border);
margin: 6px 0;
flex-shrink: 0;
}
.uc-menu-name {
height: 7px;
flex: 1;
border-radius: 3px;
background: var(--color-border);
}
.uc-menu-name--sm { max-width: 55%; }
.uc-menu-price {
height: 7px;
width: 22px;
border-radius: 3px;
background: var(--color-border);
flex-shrink: 0;
}
.uc-menu-row--highlight {
background: color-mix(in srgb, var(--color-primary) 15%, transparent);
animation: uc-menu-highlight 3s ease-in-out infinite;
}
.uc-menu-row--highlight .uc-menu-name,
.uc-menu-row--highlight .uc-menu-price {
background: var(--color-primary);
animation: uc-menu-price-update 3s ease-in-out infinite;
}
@keyframes uc-menu-highlight {
0%, 100% { background: color-mix(in srgb, var(--color-primary) 15%, transparent); }
45%, 55% { background: color-mix(in srgb, var(--color-accent) 25%, transparent); }
}
@keyframes uc-menu-price-update {
0%, 38% { opacity: 1; }
42% { opacity: 0; }
48% { opacity: 1; }
52% { opacity: 0; }
58%, 100%{ opacity: 1; }
}
/* ══════════════════════════════════
2 — EVENT DISPLAY
══════════════════════════════════ */
.uc-inner--event { gap: 0; padding: 16px 18px; }
.uc-event-header {
width: 100%;
height: 13px;
border-radius: 3px;
background: var(--color-border);
margin-bottom: 10px;
flex-shrink: 0;
position: relative;
overflow: hidden;
}
.uc-event-header::after {
content: '';
position: absolute;
top: 0; left: 0;
height: 100%;
width: 45%;
background: var(--color-primary);
border-radius: 3px;
animation: uc-event-hdr-move 4s ease-in-out infinite;
}
.uc-event-row {
width: 100%;
display: flex;
align-items: center;
gap: 8px;
flex-shrink: 0;
}
.uc-event-row + .uc-event-row { margin-top: 7px; }
.uc-event-time {
height: 7px;
width: 22px;
border-radius: 2px;
background: var(--color-border);
flex-shrink: 0;
}
.uc-event-bar {
height: 14px;
flex: 1;
border-radius: 3px;
background: var(--color-border);
position: relative;
overflow: hidden;
}
.uc-event-bar::after {
content: '';
position: absolute;
inset: 0;
background: linear-gradient(90deg, transparent 0%, rgba(255,255,255,.3) 50%, transparent 100%);
transform: translateX(-150%);
}
.uc-event-bar--active { background: var(--color-primary); }
.uc-event-bar--accent { background: color-mix(in srgb, var(--color-accent) 55%, var(--color-border)); }
.uc-event-bar--active::after { animation: uc-event-shimmer 2.2s linear infinite; }
.uc-event-bar--accent::after { animation: uc-event-shimmer 2.2s linear infinite 1.1s; }
.uc-event-cursor {
position: absolute;
top: 32px;
left: 26px;
width: 1.5px;
height: calc(100% - 44px);
background: var(--color-accent);
opacity: .75;
pointer-events: none;
animation: uc-event-cursor-move 4s ease-in-out infinite;
}
@keyframes uc-event-hdr-move {
0%, 20% { transform: translateX(0%); }
55%, 80% { transform: translateX(125%); }
100% { transform: translateX(0%); }
}
@keyframes uc-event-shimmer {
0% { transform: translateX(-150%); }
100% { transform: translateX(250%); }
}
@keyframes uc-event-cursor-move {
0%, 10% { left: 26px; opacity: .5; }
50% { left: calc(100% - 22px); opacity: .9; }
90%, 100%{ left: 26px; opacity: .5; }
}
/* ══════════════════════════════════
3 — OFFICE DASHBOARD
══════════════════════════════════ */
.uc-inner--dashboard {
justify-content: flex-end;
padding: 18px 16px 12px;
}
.uc-db-bars {
display: flex;
align-items: stretch;
gap: 9px;
width: 100%;
height: 80px;
}
.uc-db-bar {
flex: 1;
border-radius: 4px 4px 0 0;
transform-origin: bottom;
transform: scaleY(.55);
}
.uc-db-bar--1, .uc-db-bar--3 { background: var(--color-primary); }
.uc-db-bar--2, .uc-db-bar--4 { background: color-mix(in srgb, var(--color-primary) 45%, var(--color-border)); }
.uc-db-bar--1 { animation: uc-db-scale-1 3s ease-in-out infinite; }
.uc-db-bar--2 { animation: uc-db-scale-2 3s ease-in-out infinite .4s; }
.uc-db-bar--3 { animation: uc-db-scale-3 3s ease-in-out infinite .8s; }
.uc-db-bar--4 { animation: uc-db-scale-4 3s ease-in-out infinite 1.2s; }
.uc-db-baseline {
width: 100%;
height: 1px;
background: var(--color-border);
flex-shrink: 0;
margin-bottom: 6px;
}
.uc-db-labels {
display: flex;
gap: 9px;
width: 100%;
flex-shrink: 0;
}
.uc-db-lbl {
flex: 1;
height: 5px;
border-radius: 2px;
background: var(--color-border);
opacity: .6;
}
@keyframes uc-db-scale-1 {
0%, 100% { transform: scaleY(.55); }
40% { transform: scaleY(.82); }
70% { transform: scaleY(.42); }
}
@keyframes uc-db-scale-2 {
0%, 100% { transform: scaleY(.70); }
40% { transform: scaleY(.38); }
70% { transform: scaleY(.78); }
}
@keyframes uc-db-scale-3 {
0%, 100% { transform: scaleY(.40); }
40% { transform: scaleY(.68); }
70% { transform: scaleY(.58); }
}
@keyframes uc-db-scale-4 {
0%, 100% { transform: scaleY(.62); }
40% { transform: scaleY(.50); }
70% { transform: scaleY(.88); }
}
/* ══════════════════════════════════
4 — WAYFINDING
══════════════════════════════════ */
.uc-inner--wayfinding { padding: 0; }
/* Corridor lines */
.uc-wf-corridor {
position: absolute;
background: var(--color-border);
}
.uc-wf-corridor--h {
height: 8px;
width: 70%;
top: 50%;
left: 15%;
transform: translateY(-50%);
border-radius: 2px;
}
.uc-wf-corridor--v {
width: 8px;
height: 54%;
left: 50%;
top: 23%;
transform: translateX(-50%);
border-radius: 2px;
}
/* Rooms */
.uc-wf-room {
position: absolute;
border: 1.5px solid var(--color-border);
border-radius: 3px;
background: var(--color-bg-alt);
}
.uc-wf-room--1 { width: 32px; height: 28px; top: 17%; left: 17%; }
.uc-wf-room--2 { width: 28px; height: 26px; top: 17%; right: 17%; }
.uc-wf-room--3 { width: 30px; height: 28px; bottom: 17%; left: 50%; transform: translateX(-50%); }
/* Location dot */
.uc-wf-dot {
position: absolute;
width: 12px;
height: 12px;
border-radius: 50%;
background: var(--color-primary);
top: 50%;
left: 36%;
transform: translate(-50%, -50%);
}
.uc-wf-dot::after {
content: '';
position: absolute;
inset: -5px;
border-radius: 50%;
border: 1.5px solid var(--color-primary);
animation: uc-wf-pulse 2s ease-out infinite;
}
/* Direction arrow */
.uc-wf-arrow {
position: absolute;
top: 50%;
left: 36%;
transform: translate(-50%, calc(-50% - 18px));
width: 0;
height: 0;
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-bottom: 11px solid var(--color-accent);
animation: uc-wf-arrow-bob 1.8s ease-in-out infinite;
}
@keyframes uc-wf-pulse {
0% { transform: scale(1); opacity: .7; }
70% { transform: scale(2.4); opacity: 0; }
100% { transform: scale(1); opacity: 0; }
}
@keyframes uc-wf-arrow-bob {
0%, 100% { transform: translate(-50%, calc(-50% - 18px)); }
50% { transform: translate(-50%, calc(-50% - 24px)); }
}
/* ── Responsive ──────────────────────────────────────────── */
@media (max-width: 900px) {
.uc-track { flex-wrap: wrap; justify-content: center; gap: 2rem; padding: 2.5rem 2rem; }
.uc-item { width: calc(50% - 1rem); flex: none; }
}
@media (max-width: 500px) {
.uc-track { padding: 2rem 1.5rem; }
.uc-item { width: 100%; }
.uc-item-desc { max-width: none; }
}
/* ── Reduced-motion overrides ────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
.uc-menu-row--highlight,
.uc-menu-row--highlight .uc-menu-name,
.uc-menu-row--highlight .uc-menu-price,
.uc-event-header::after,
.uc-event-bar--active::after,
.uc-event-bar--accent::after,
.uc-event-cursor,
.uc-db-bar--1, .uc-db-bar--2, .uc-db-bar--3, .uc-db-bar--4,
.uc-wf-dot::after,
.uc-wf-arrow { animation: none !important; }
/* Static bar heights when animation is off */
.uc-db-bar--1 { transform: scaleY(.55) !important; }
.uc-db-bar--2 { transform: scaleY(.70) !important; }
.uc-db-bar--3 { transform: scaleY(.40) !important; }
.uc-db-bar--4 { transform: scaleY(.62) !important; }
}

View File

@@ -1763,7 +1763,6 @@ reg('oribi/hero-animated', {
save: function () { return null; }
});
/* ANIMATED PAGE HERO ──────────────────────────────────────────────────── */
reg('oribi/page-hero-animated', {
title: 'Animated Page Hero',
icon: 'flag',
@@ -1846,4 +1845,81 @@ reg('oribi/site-footer', {
save: function () { return null; }
});
/* USE CASES SHOWCASE ──────────────────────────────────────────────────── */
reg('oribi/use-cases', {
title: 'Use Cases Showcase',
icon: 'grid-view',
category: 'oribi',
supports: { html: false },
attributes: {
label: { type: 'string', default: '\u25cf Use Cases' },
heading: { type: 'string', default: 'Built for Every Scenario' },
lead: { type: 'string', default: 'From menus to meeting-room dashboards, digital signage adapts to your environment.' },
case1Title: { type: 'string', default: 'Menu Boards' },
case1Desc: { type: 'string', default: 'Showcase food, drinks, and daily specials with dynamic, always-current displays.' },
case2Title: { type: 'string', default: 'Event Displays' },
case2Desc: { type: 'string', default: 'Promote schedules, speakers, and live countdowns across lobbies and venues.' },
case3Title: { type: 'string', default: 'Office Dashboards' },
case3Desc: { type: 'string', default: 'Surface KPIs, occupancy data, and team alerts on screens throughout your workspace.' },
case4Title: { type: 'string', default: 'Wayfinding' },
case4Desc: { type: 'string', default: 'Guide visitors through complex buildings with interactive maps and directional signs.' },
},
edit: function (props) {
var a = props.attributes, s = props.setAttributes;
return el(Frag, null,
el(IC, null,
el(PB, { title: 'Section Header' },
el(TC, { label: 'Label', value: a.label, onChange: function(v){s({label:v});} }),
el(TC, { label: 'Heading', value: a.heading, onChange: function(v){s({heading:v});} }),
el(TA, { label: 'Lead', value: a.lead, onChange: function(v){s({lead:v});} })
),
el(PB, { title: 'Use Case 1 Menu Boards', initialOpen: false },
el(TC, { label: 'Title', value: a.case1Title, onChange: function(v){s({case1Title:v});} }),
el(TA, { label: 'Description', value: a.case1Desc, onChange: function(v){s({case1Desc:v});} })
),
el(PB, { title: 'Use Case 2 Events', initialOpen: false },
el(TC, { label: 'Title', value: a.case2Title, onChange: function(v){s({case2Title:v});} }),
el(TA, { label: 'Description', value: a.case2Desc, onChange: function(v){s({case2Desc:v});} })
),
el(PB, { title: 'Use Case 3 Dashboards', initialOpen: false },
el(TC, { label: 'Title', value: a.case3Title, onChange: function(v){s({case3Title:v});} }),
el(TA, { label: 'Description', value: a.case3Desc, onChange: function(v){s({case3Desc:v});} })
),
el(PB, { title: 'Use Case 4 Wayfinding', initialOpen: false },
el(TC, { label: 'Title', value: a.case4Title, onChange: function(v){s({case4Title:v});} }),
el(TA, { label: 'Description', value: a.case4Desc, onChange: function(v){s({case4Desc:v});} })
)
),
el('section', { className: 'section use-cases-section' },
el('div', { className: 'container' },
el('div', { className: 'section-header' },
a.label ? el('span', { className: 'section-label' }, a.label) : null,
a.heading ? el('h2', null, a.heading) : null,
a.lead ? el('p', { className: 'lead' }, a.lead) : null
),
el('div', { className: 'uc-track' },
['menu', 'event', 'dashboard', 'wayfinding'].map(function (mod, i) {
var n = i + 1;
var title = a['case' + n + 'Title'];
var desc = a['case' + n + 'Desc'];
return el('div', { key: mod, className: 'uc-item' },
el('div', { className: 'uc-circle uc-anim--' + mod,
style: { display:'flex', alignItems:'center', justifyContent:'center',
fontSize:'.7rem', color:'var(--color-muted)', textAlign:'center', padding:'8px' } },
mod
),
el('div', { className: 'uc-item-body' },
el('div', { className: 'uc-item-title' }, title),
desc ? el('p', { className: 'uc-item-desc' }, desc) : null
)
);
})
)
)
)
);
},
save: function () { return null; }
});
})(window.wp);

View File

@@ -656,6 +656,25 @@ add_action( 'init', function () {
'render_callback' => 'oribi_render_page_hero_animated',
] );
/* Use Cases Showcase - 4 animated circles beneath the homepage hero */
register_block_type( 'oribi/use-cases', [
'attributes' => [
'label' => [ 'type' => 'string', 'default' => '● Use Cases' ],
'heading' => [ 'type' => 'string', 'default' => 'Built for Every Scenario' ],
'lead' => [ 'type' => 'string', 'default' => 'From menus to meeting-room dashboards, digital signage adapts to your environment.' ],
'case1Title' => [ 'type' => 'string', 'default' => 'Menu Boards' ],
'case1Desc' => [ 'type' => 'string', 'default' => 'Showcase food, drinks, and daily specials with dynamic, always-current displays.' ],
'case2Title' => [ 'type' => 'string', 'default' => 'Event Displays' ],
'case2Desc' => [ 'type' => 'string', 'default' => 'Promote schedules, speakers, and live countdowns across lobbies and venues.' ],
'case3Title' => [ 'type' => 'string', 'default' => 'Office Dashboards' ],
'case3Desc' => [ 'type' => 'string', 'default' => 'Surface KPIs, occupancy data, and team alerts on screens throughout your workspace.' ],
'case4Title' => [ 'type' => 'string', 'default' => 'Wayfinding' ],
'case4Desc' => [ 'type' => 'string', 'default' => 'Guide visitors through complex buildings with interactive maps and directional signs.' ],
],
'supports' => $block_supports,
'render_callback' => 'oribi_render_use_cases',
] );
} );
/* ══════════════════════════════════════════════════════════════════════════════
@@ -1762,3 +1781,95 @@ function oribi_render_page_hero_animated( $a ) {
</section>
<?php return ob_get_clean();
}
/* ── Use Cases Showcase ────────────────────────────────────────────────── */
function oribi_uc_anim_inner( $mod ) {
switch ( $mod ) {
case 'menu':
return '
<div class="uc-inner uc-inner--menu" aria-hidden="true">
<div class="uc-menu-header"></div>
<div class="uc-menu-row"><span class="uc-menu-name"></span><span class="uc-menu-price"></span></div>
<div class="uc-menu-row uc-menu-row--highlight"><span class="uc-menu-name"></span><span class="uc-menu-price"></span></div>
<div class="uc-menu-row"><span class="uc-menu-name uc-menu-name--sm"></span><span class="uc-menu-price"></span></div>
<div class="uc-menu-row"><span class="uc-menu-name"></span><span class="uc-menu-price"></span></div>
<div class="uc-menu-divider"></div>
<div class="uc-menu-row"><span class="uc-menu-name uc-menu-name--sm"></span><span class="uc-menu-price"></span></div>
</div>';
case 'event':
return '
<div class="uc-inner uc-inner--event" aria-hidden="true">
<div class="uc-event-cursor"></div>
<div class="uc-event-header"></div>
<div class="uc-event-row"><div class="uc-event-time"></div><div class="uc-event-bar uc-event-bar--active"></div></div>
<div class="uc-event-row"><div class="uc-event-time"></div><div class="uc-event-bar"></div></div>
<div class="uc-event-row"><div class="uc-event-time"></div><div class="uc-event-bar uc-event-bar--accent"></div></div>
<div class="uc-event-row"><div class="uc-event-time"></div><div class="uc-event-bar"></div></div>
</div>';
case 'dashboard':
return '
<div class="uc-inner uc-inner--dashboard" aria-hidden="true">
<div class="uc-db-bars">
<div class="uc-db-bar uc-db-bar--1"></div>
<div class="uc-db-bar uc-db-bar--2"></div>
<div class="uc-db-bar uc-db-bar--3"></div>
<div class="uc-db-bar uc-db-bar--4"></div>
</div>
<div class="uc-db-baseline"></div>
<div class="uc-db-labels">
<div class="uc-db-lbl"></div>
<div class="uc-db-lbl"></div>
<div class="uc-db-lbl"></div>
<div class="uc-db-lbl"></div>
</div>
</div>';
case 'wayfinding':
return '
<div class="uc-inner uc-inner--wayfinding" aria-hidden="true">
<div class="uc-wf-corridor uc-wf-corridor--h"></div>
<div class="uc-wf-corridor uc-wf-corridor--v"></div>
<div class="uc-wf-room uc-wf-room--1"></div>
<div class="uc-wf-room uc-wf-room--2"></div>
<div class="uc-wf-room uc-wf-room--3"></div>
<div class="uc-wf-dot"></div>
<div class="uc-wf-arrow"></div>
</div>';
default:
return '';
}
}
function oribi_render_use_cases( $a ) {
$cases = [
[ 'title' => $a['case1Title'], 'desc' => $a['case1Desc'], 'mod' => 'menu' ],
[ 'title' => $a['case2Title'], 'desc' => $a['case2Desc'], 'mod' => 'event' ],
[ 'title' => $a['case3Title'], 'desc' => $a['case3Desc'], 'mod' => 'dashboard' ],
[ 'title' => $a['case4Title'], 'desc' => $a['case4Desc'], 'mod' => 'wayfinding' ],
];
ob_start(); ?>
<section class="section use-cases-section">
<div class="container">
<?php if ( $a['label'] || $a['heading'] || $a['lead'] ) : ?>
<div class="section-header">
<?php if ( $a['label'] ) : ?><span class="section-label"><?php echo esc_html( $a['label'] ); ?></span><?php endif; ?>
<?php if ( $a['heading'] ) : ?><h2><?php echo wp_kses_post( $a['heading'] ); ?></h2><?php endif; ?>
<?php if ( $a['lead'] ) : ?><p class="lead"><?php echo wp_kses_post( $a['lead'] ); ?></p><?php endif; ?>
</div>
<?php endif; ?>
<div class="uc-track">
<?php foreach ( $cases as $c ) : ?>
<div class="uc-item">
<div class="uc-circle uc-anim--<?php echo esc_attr( $c['mod'] ); ?>">
<?php echo oribi_uc_anim_inner( $c['mod'] ); ?>
</div>
<div class="uc-item-body">
<div class="uc-item-title"><?php echo esc_html( $c['title'] ); ?></div>
<?php if ( $c['desc'] ) : ?><p class="uc-item-desc"><?php echo esc_html( $c['desc'] ); ?></p><?php endif; ?>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
</section>
<?php return ob_get_clean();
}