Add TV stick animation and rendering support for platform rows

This commit is contained in:
Matt Batchelder
2026-02-21 09:43:58 -05:00
parent 4591578cb2
commit d68c2c1d31
5 changed files with 284 additions and 5 deletions

View File

@@ -12,7 +12,7 @@
<!-- wp:oribi/platform-section {"label":"Core Features","heading":"Everything You Need, Nothing You Don\u0027t","lead":"Create, schedule, and manage digital signage content from a single dashboard — whether you have one screen or one thousand."} -->
<!-- wp:oribi/platform-row {"heading":"One Dashboard for Every Display","description":"Manage your entire signage network from a single cloud-based console. Organise screens by location, group, or purpose. Push content updates across your whole estate in one click — no matter how many sites you operate.","btnText":"Get Started","btnUrl":"/contact"} /-->
<!-- wp:oribi/platform-row {"heading":"Scheduling That Runs Itself","description":"Set content to appear at the right time, in the right place, automatically. Day-parting, date ranges, and event-triggered playback let you plan weeks ahead while the platform handles the execution.","btnText":"See Pricing","btnUrl":"/pricing","reversed":true} /-->
<!-- wp:oribi/platform-row {"heading":"Works With Your Existing Screens","description":"Our player devices connect to any screen with an HDMI port — no proprietary hardware required. Already have displays? Plug in and go. Need a full setup? We offer bundled player-and-display packages too.","btnText":"View Devices","btnUrl":"/devices"} /-->
<!-- wp:oribi/platform-row {"heading":"Works With Your Existing Screens","description":"Our player devices connect to any screen with an HDMI port — no proprietary hardware required. Already have displays? Plug in and go. Need a full setup? We offer bundled player-and-display packages too.","btnText":"View Devices","btnUrl":"/devices","tvStick":true} /-->
<!-- wp:oribi/platform-row {"heading":"Live Data, Straight to Screen","description":"Pull in web dashboards, social feeds, KPIs, and real-time APIs directly to your displays. Content updates automatically — no manual refreshing, no extra steps. Turn any screen into a live information hub.","btnText":"Learn More","btnUrl":"/solutions","reversed":true} /-->
<!-- /wp:oribi/platform-section -->

View File

@@ -11,7 +11,7 @@ return <<<'ORIBI_SYNC_CONTENT'
<!-- wp:oribi/platform-section {"label":"The Complete Package","heading":"Everything You Need for Engaging Digital Signage","lead":"High-quality visuals, real-time data, and reliable playback — all managed from one powerful platform."} -->
<!-- wp:oribi/platform-row {"heading":"Professional Content Creation","description":"Our in-house photography and video production services showcase your products, services, and environment with polished, engaging visuals. From digital menu boards to branded promotions, we create content that captures attention.","btnText":"See Features","btnUrl":"/features"} /-->
<!-- wp:oribi/platform-row {"heading":"Live Data \u0026amp; Web Dashboards","description":"Integrate your existing web dashboards, social feeds, and real-time data sources directly to your displays. Bring your most important information to life on screen, automatically and effortlessly.","btnUrl":"/features","reversed":true} /-->
<!-- wp:oribi/platform-row {"heading":"Live Data \u0026amp; Web Dashboards","description":"Integrate your existing web dashboards, social feeds, and real-time data sources directly to your displays. Bring your most important information to life on screen, automatically and effortlessly.","btnUrl":"/features","reversed":true,"isDashboard":true} /-->
<!-- wp:oribi/platform-row {"heading":"Reliable on Any Screen","description":"Our intelligent player devices work on any screen with HDMI, and keep your message running even when the internet goes down. Enterprise-grade hardware designed for uninterrupted, always-on signage.","btnText":"View Devices","btnUrl":"/devices","deviceAnim":true} /-->
<!-- /wp:oribi/platform-section -->

View File

@@ -2396,6 +2396,236 @@ p:last-child { margin-bottom: 0; }
.da-screen::after { animation: none; }
}
/* ── 10c. TV Stick Plug Animation ──────────────────────────── */
.platform-visual.has-tv-stick {
background: none !important;
border: none !important;
border-radius: 0;
aspect-ratio: unset;
padding: 0;
overflow: visible;
position: relative;
font-size: inherit;
}
.ts-stage {
position: relative;
width: 100%;
aspect-ratio: 4/3;
display: flex;
align-items: center;
justify-content: center;
}
/* ── TV ── */
.ts-tv {
display: flex;
flex-direction: column;
align-items: center;
position: relative;
}
.ts-tv__body {
width: 320px;
height: 188px;
background: var(--color-bg-alt);
border: 4px solid var(--color-bg-alt);
border-radius: 6px 6px 4px 4px;
outline: 1px solid var(--color-border);
padding: 3px;
display: flex;
align-items: stretch;
position: relative;
box-shadow: 0 14px 48px rgba(0,0,0,0.55);
}
.ts-tv__screen {
width: 100%;
height: 100%;
border-radius: 2px;
position: relative;
overflow: hidden;
background:
repeating-linear-gradient(
180deg,
transparent,
transparent 3px,
rgba(0,0,0,0.10) 3px,
rgba(0,0,0,0.10) 4px
),
linear-gradient(135deg, #0c1016 0%, #151c28 60%, #0c1016 100%);
}
/* Subtle green ambient glow on screen */
.ts-tv__screen::before {
content: '';
position: absolute;
top: -20%;
left: -10%;
width: 60%;
height: 60%;
background: radial-gradient(ellipse, rgba(74,222,128,0.12) 0%, transparent 70%);
pointer-events: none;
}
/* Scan line on screen */
.ts-tv__screen::after {
content: '';
position: absolute;
left: 0;
width: 100%;
height: 3px;
background: linear-gradient(90deg, transparent, rgba(74,222,128,0.28), transparent);
animation: da-scan 3s linear infinite;
pointer-events: none;
}
/* Screen glow when stick plugs in */
.ts-stage.is-plugged .ts-tv__screen {
animation: ts-screen-glow 0.8s ease 0.1s both;
}
@keyframes ts-screen-glow {
0% { filter: brightness(1); }
40% { filter: brightness(1.25); }
100% { filter: brightness(1); }
}
/* HDMI port on back-right of TV */
.ts-tv__port {
position: absolute;
right: -6px;
top: 50%;
transform: translateY(-50%);
width: 6px;
height: 14px;
background: #1a1a1a;
border: 1px solid var(--color-border);
border-left: none;
border-radius: 0 2px 2px 0;
z-index: 1;
}
.ts-tv__port::before {
content: '';
position: absolute;
left: 0;
top: 2px;
width: 3px;
height: 8px;
background: #333;
border-radius: 0 1px 1px 0;
}
/* Feet */
.ts-tv__feet {
display: flex;
justify-content: space-between;
width: 180px;
}
.ts-tv__foot {
width: 12px;
height: 8px;
background: var(--color-bg-alt);
border: 1px solid var(--color-border);
border-radius: 0 0 4px 4px;
}
/* ── Stick device ── */
.ts-stick {
position: absolute;
right: -20px;
top: 50%;
transform: translateY(-50%) translateX(80px);
display: flex;
align-items: center;
opacity: 0;
z-index: 2;
}
.ts-stick__body {
width: 68px;
height: 26px;
background: linear-gradient(180deg, #f5f5f5, #e0e0e0);
border: 1px solid #ccc;
border-radius: 5px;
position: relative;
box-shadow:
0 2px 8px rgba(0,0,0,0.25),
inset 0 1px 0 rgba(255,255,255,0.6);
}
/* Brand logo area subtle inset */
.ts-stick__body::before {
content: '';
position: absolute;
top: 6px;
left: 10px;
width: 28px;
height: 12px;
background: rgba(0,0,0,0.04);
border-radius: 2px;
}
/* LED indicator */
.ts-stick__led {
position: absolute;
right: 8px;
top: 50%;
transform: translateY(-50%);
width: 4px;
height: 4px;
background: #999;
border-radius: 50%;
transition: background 0.4s ease, box-shadow 0.4s ease;
}
.ts-stage.is-plugged .ts-stick__led {
background: #4CAF50;
box-shadow: 0 0 6px rgba(76,175,80,0.8);
}
/* HDMI connector */
.ts-stick__connector {
width: 14px;
height: 10px;
background: linear-gradient(180deg, #888, #666);
border-radius: 0 2px 2px 0;
margin-left: -1px;
position: relative;
box-shadow: 0 1px 3px rgba(0,0,0,0.3);
}
.ts-stick__connector::before {
content: '';
position: absolute;
left: 2px;
top: 2px;
width: 6px;
height: 6px;
background: #555;
border-radius: 1px;
}
/* ── Plug-in animation ── */
.ts-stage.is-animating .ts-stick {
animation: ts-slide-in 1.4s cubic-bezier(0.22, 0.61, 0.36, 1) forwards;
}
@keyframes ts-slide-in {
0% { opacity: 0; transform: translateY(-50%) translateX(80px); }
20% { opacity: 1; transform: translateY(-50%) translateX(60px); }
70% { transform: translateY(-50%) translateX(8px); }
85% { transform: translateY(-50%) translateX(12px); }
100% { opacity: 1; transform: translateY(-50%) translateX(6px); }
}
/* Responsive scale-down */
@media (max-width: 900px) {
.ts-tv__body { width: 260px; height: 152px; }
.ts-tv__feet { width: 140px; }
.ts-stick__body { width: 56px; height: 22px; }
}
@media (max-width: 640px) {
.ts-tv__body { width: 200px; height: 118px; }
.ts-tv__feet { width: 110px; }
.ts-stick__body { width: 46px; height: 18px; }
.ts-stick__connector { width: 10px; height: 8px; }
.ts-stick__body::before { top: 4px; left: 6px; width: 20px; height: 8px; }
.ts-stick__led { width: 3px; height: 3px; right: 5px; }
}
@media (prefers-reduced-motion: reduce) {
.ts-stage .ts-stick {
opacity: 1;
transform: translateY(-50%) translateX(6px);
}
.ts-stage.is-animating .ts-stick { animation: none; }
.ts-stage.is-plugged .ts-tv__screen { animation: none; }
.ts-stage.is-plugged .ts-stick__led {
background: #4CAF50;
box-shadow: 0 0 6px rgba(76,175,80,0.8);
}
}
/* ── 11. Page Hero (inner pages) ───────────────────────────── */
.page-hero {
background: #111111;

View File

@@ -675,3 +675,34 @@ document.addEventListener('DOMContentLoaded', () => {
}
});
})();
/* ── TV Stick Plug Animation ─────────────────────────────────────────────── */
(function () {
var stages = document.querySelectorAll('[data-tv-stick-anim]');
if (!stages.length) return;
// Honour reduced-motion: show plugged-in state immediately
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
stages.forEach(function (stage) {
stage.classList.add('is-plugged');
});
return;
}
if ('IntersectionObserver' in window) {
var io = new IntersectionObserver(function (entries) {
entries.forEach(function (e) {
if (e.isIntersecting) {
var stage = e.target;
stage.classList.add('is-animating');
// Add plugged state after slide-in completes (1.4s)
setTimeout(function () {
stage.classList.add('is-plugged');
}, 1400);
io.unobserve(stage);
}
});
}, { threshold: 0.3 });
stages.forEach(function (stage) { io.observe(stage); });
}
})();

View File

@@ -554,6 +554,7 @@ add_action( 'init', function () {
'imgAlt' => [ 'type' => 'string', 'default' => '' ],
'imgWidth' => [ 'type' => 'number', 'default' => 300 ],
'deviceAnim' => [ 'type' => 'boolean', 'default' => false ],
'tvStick' => [ 'type' => 'boolean', 'default' => false ],
],
'supports' => $block_supports,
'render_callback' => 'oribi_render_platform_row',
@@ -1339,9 +1340,8 @@ function oribi_render_platform_row( $a ) {
$img_alt = ! empty( $a['imgAlt'] ) ? $a['imgAlt'] : '';
$img_w = ! empty( $a['imgWidth'] ) ? intval( $a['imgWidth'] ) : 300;
// Check if this is a dashboard card (by heading content or dashboard flag)
$heading_text = $a['heading'] ?? '';
$is_dashboard = ! empty( $a['isDashboard'] ) || stripos( $heading_text, 'dashboard' ) !== false || stripos( $heading_text, 'data' ) !== false;
// Only render animated dashboard when explicitly flagged
$is_dashboard = ! empty( $a['isDashboard'] );
if ( $is_dashboard ) {
// Render animated dashboard chart SVG
@@ -1480,6 +1480,24 @@ function oribi_render_platform_row( $a ) {
$da .= '</div>';
$visual_html = $da;
$visual_cls = 'platform-visual has-anim';
} elseif ( ! empty( $a['tvStick'] ) ) {
$ts = '<div class="ts-stage" data-tv-stick-anim aria-hidden="true">';
$ts .= '<div class="ts-tv">';
$ts .= '<div class="ts-tv__body">';
$ts .= '<div class="ts-tv__screen"></div>';
$ts .= '<div class="ts-tv__port"></div>';
$ts .= '</div>';
$ts .= '<div class="ts-tv__feet"><div class="ts-tv__foot"></div><div class="ts-tv__foot"></div></div>';
$ts .= '</div>';
$ts .= '<div class="ts-stick">';
$ts .= '<div class="ts-stick__body">';
$ts .= '<div class="ts-stick__led"></div>';
$ts .= '</div>';
$ts .= '<div class="ts-stick__connector"></div>';
$ts .= '</div>';
$ts .= '</div>';
$visual_html = $ts;
$visual_cls = 'platform-visual has-tv-stick';
} else {
$visual_html = oribi_render_icon( $a['visual'] ?? '' );
$visual_cls = 'platform-visual';