Loading...
Loading...
Weather graphics are a broadcast staple — from local news segments to national forecasts. This card shows current conditions with a large temperature display, plus a multi-day forecast row. It demonstrates how to handle complex nested data schemas with arrays and multiple sections in a single graphic.
Nested data schema
The data includes scalar fields (location, temp) alongside an array of forecast objects — each with day, icon, high, and low. This is the most complex schema in the tutorials.
Emoji as icons
Uses Unicode emoji for weather icons instead of SVGs or icon fonts. Zero dependencies, works everywhere, and viewers instantly recognize them.
Multi-section layout
Two distinct visual zones: the main current-conditions section (large icon + temp) and the compact forecast row below — each animating independently.
The _renderForecast method iterates over the forecast array, creating a compact card for each day. Each card shows the day name, an emoji icon, and high/low temperatures. The staggered delay creates a left-to-right reveal.
_renderForecast(forecast) {
const row = this.querySelector('.forecast-row');
row.innerHTML = '';
forecast.forEach((day, i) => {
const card = document.createElement('div');
card.className = 'forecast-day';
card.style.transitionDelay = `${(i * 100) + 400}ms`;
// Convert hex code point to emoji
const emoji = String.fromCodePoint(parseInt(day.icon, 16));
card.innerHTML = `
<div class="forecast-day-name">${day.day}</div>
<div class="forecast-icon">${emoji}</div>
<div class="forecast-temps">
<span class="forecast-high">${day.high}°</span>
<span class="forecast-low">${day.low}°</span>
</div>
`;
row.appendChild(card);
});
}
async load({ data }) {
if (data?.location) this._location.textContent = data.location;
if (data?.temp) this._temp.textContent = data.temp + '°';
if (data?.condition) this._condition.textContent = data.condition;
if (data?.icon) {
this._icon.textContent = String.fromCodePoint(
parseInt(data.icon, 16)
);
}
if (data?.forecast) this._renderForecast(data.forecast);
return { statusCode: 200 };
}
async playAction() {
this._root.classList.add('visible');
await new Promise(r => setTimeout(r, 1000));
return { statusCode: 200, currentStep: 0 };
}The weather card uses a two-part vertical layout. The main section holds the current conditions large and prominent, while the forecast row is compact with evenly-spaced day cards.
.weather-card {
position: absolute; /* against the graphic's root, not the viewport */
bottom: 60px;
left: 48px;
width: 380px;
background: rgba(15, 15, 30, 0.9);
backdrop-filter: blur(12px);
border-radius: 16px;
overflow: hidden;
opacity: 0;
transform: translateY(20px);
transition: opacity 0.5s ease, transform 0.5s ease;
}
.weather-card.visible {
opacity: 1;
transform: translateY(0);
}
.weather-main {
padding: 24px;
display: flex;
align-items: center;
gap: 16px;
}
.weather-temp {
font-size: 56px;
font-weight: 700;
color: white;
line-height: 1;
}
.weather-icon-large {
font-size: 48px;
}
.forecast-row {
display: flex;
justify-content: space-around;
padding: 12px 16px;
border-top: 1px solid rgba(255, 255, 255, 0.08);
}
.forecast-day {
text-align: center;
opacity: 0;
transform: translateY(8px);
transition: opacity 0.4s ease, transform 0.4s ease;
/* transition-delay set per card via JS */
}
.visible .forecast-day {
opacity: 1;
transform: translateY(0);
}
.forecast-high {
color: white;
font-weight: 600;
}
.forecast-low {
color: rgba(255, 255, 255, 0.4);
margin-left: 4px;
}Design tip
Use font-variant-numeric: tabular-nums on temperature values so digits always take the same width. Without this, numbers like "8" and "18" have different widths, causing the layout to shift when temperatures change — a common visual glitch in weather graphics.
The forecast field mixes scalar props with a typed array — that's how OGraf handles multi-section data in a single schema.
{
"$schema": "https://ograf.ebu.io/v1/specification/json-schemas/graphics/schema.json",
"id": "dev.ograf.tutorial.weather",
"version": "1.0.0",
"name": "Weather Forecast",
"description": "Weather card with current conditions and a 3-day forecast row. Tutorial from ograf.dev.",
"author": {
"name": "ograf.dev",
"url": "https://ograf.dev"
},
"main": "graphic.mjs",
"stepCount": 1,
"supportsRealTime": true,
"supportsNonRealTime": false,
"schema": {
"type": "object",
"properties": {
"location": {
"type": "string",
"title": "Location",
"gddType": "single-line",
"default": "London"
},
"temp": {
"type": "string",
"title": "Temperature",
"gddType": "single-line",
"default": "18°C"
},
"condition": {
"type": "string",
"title": "Condition",
"gddType": "single-line",
"default": "Partly Cloudy"
},
"icon": {
"type": "string",
"title": "Icon (emoji)",
"gddType": "single-line",
"default": "⛅"
},
"forecast": {
"type": "array",
"title": "Forecast",
"items": {
"type": "object",
"properties": {
"day": {
"type": "string",
"title": "Day",
"gddType": "single-line"
},
"temp": {
"type": "string",
"title": "Temp",
"gddType": "single-line"
},
"icon": {
"type": "string",
"title": "Icon",
"gddType": "single-line"
}
},
"required": [
"day",
"temp",
"icon"
]
},
"default": [
{
"day": "Tue",
"temp": "20°C",
"icon": "☀️"
},
{
"day": "Wed",
"temp": "16°C",
"icon": "🌧️"
},
{
"day": "Thu",
"temp": "19°C",
"icon": "⛅"
}
]
}
}
}
}A real OGraf Graphics Definition v1 package. A compliant renderer reads the manifest and drives the lifecycle. MIT-licensed; drop it into any OGraf-compatible system.
weather.ograf.json
Manifest — what a renderer reads (id, schema, lifecycle flags)
graphic.mjs
Web Component with load / play / update / stop / customAction / dispose
style.css
Stylesheet, loaded by graphic.mjs via a <link> tag
README.md
Usage notes
Nested data schemas, emoji icons, and a multi-section layout with staggered forecast reveal — ready for any weather segment.

Lower Third
Name & title overlay

Bug / LIVE
Corner indicator with pulse

News Ticker
Scrolling headline crawl

Full Page Quote
Cinematic full-screen typography

Election Bars
Animated percentage chart

Sport Lineup
Team roster grid

Score Bug
Live match scoreboard

Countdown Timer
Self-ticking clock

Breaking News
Full-screen urgent alert

Social Media Card
Post overlay with avatar