DrawFiddle▶ RunEdit in Fiddle
// This is a web-riented fiddle, it is designed to be published server-side.
// It accepts parameter in url, ex "?city=Paris"
// The goal is to show a perfect use-case for server-side rendering with SkiaSharp.

// For desktop/mobile you would remove fiddle api usage and set city via code.

// Weather — a card built the way a phone app would do it.
var city = Fiddle.Query.Text("city", "Novi Sad");

// ---------- condition art (inline svg: the symbols font has no weather glyphs) ----------
string Sun(string fill) =>
    "<circle cx='50' cy='50' r='19' fill='" + fill + "'/>" +
    "<g stroke='" + fill + "' stroke-width='7' stroke-linecap='round'>" +
    "<path d='M50 8 v12'/><path d='M50 80 v12'/><path d='M8 50 h12'/><path d='M80 50 h12'/>" +
    "<path d='M20 20 l9 9'/><path d='M71 71 l9 9'/><path d='M80 20 l-9 9'/><path d='M29 71 l-9 9'/></g>";

string Cloud(string fill, double dy) =>
    "<g transform='translate(0," + dy.ToString(System.Globalization.CultureInfo.InvariantCulture) + ")'>" +
    "<circle cx='38' cy='52' r='18' fill='" + fill + "'/>" +
    "<circle cx='60' cy='46' r='23' fill='" + fill + "'/>" +
    "<rect x='26' y='52' width='52' height='22' rx='11' fill='" + fill + "'/></g>";

string Drops(string fill) =>
    "<g fill='" + fill + "'>" +
    "<path d='M36 78 l5 9 a5.5 5.5 0 1 1 -10 0 z'/>" +
    "<path d='M52 82 l5 9 a5.5 5.5 0 1 1 -10 0 z'/>" +
    "<path d='M68 78 l5 9 a5.5 5.5 0 1 1 -10 0 z'/></g>";

string Flakes(string fill) =>
    "<g stroke='" + fill + "' stroke-width='4' stroke-linecap='round'>" +
    "<path d='M38 80 v10'/><path d='M33 85 h10'/><path d='M56 84 v10'/><path d='M51 89 h10'/>" +
    "<path d='M74 80 v10'/><path d='M69 85 h10'/></g>";

string Bolt(string fill) =>
    "<path d='M56 70 L40 94 h11 l-5 16 20 -26 h-11 z' fill='" + fill + "'/>";

string Icon(string kind)
{
    var body = kind switch
    {
        "clear"  => Sun("#FFC94A"),
        "partly" => Sun("#FFC94A") + Cloud("#E2E8F0", 8),
        "cloudy" => Cloud("#CBD5E1", 0) + Cloud("#94A3B8", 14),
        "fog"    => Cloud("#CBD5E1", 0) +
                    "<g stroke='#94A3B8' stroke-width='5' stroke-linecap='round'><path d='M28 84 h44'/><path d='M34 94 h32'/></g>",
        "rain"   => Cloud("#B6C6DA", 0) + Drops("#7DD3FC"),
        "snow"   => Cloud("#DCE7F3", 0) + Flakes("#BAE6FD"),
        "storm"  => Cloud("#A6B4C8", 0) + Bolt("#FBBF24"),
        _        => Cloud("#CBD5E1", 0),
    };
    return "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 120'>" + body + "</svg>";
}

(string Kind, string Words) Describe(int code) => code switch
{
    0 => ("clear", "Clear sky"),
    1 => ("partly", "Mainly clear"),
    2 => ("partly", "Partly cloudy"),
    3 => ("cloudy", "Overcast"),
    45 or 48 => ("fog", "Fog"),
    51 or 53 or 55 or 56 or 57 => ("rain", "Drizzle"),
    61 or 63 or 65 or 66 or 67 => ("rain", "Rain"),
    71 or 73 or 75 or 77 => ("snow", "Snow"),
    80 or 81 or 82 => ("rain", "Rain showers"),
    85 or 86 => ("snow", "Snow showers"),
    95 or 96 or 99 => ("storm", "Thunderstorm"),
    _ => ("cloudy", "Cloudy"),
};

(string Top, string Bottom) Sky(string kind) => kind switch
{
    "clear"  => ("#2E6FD9", "#7FB2F0"),
    "partly" => ("#37699E", "#8FB6D8"),
    "rain"   => ("#2B3F55", "#5C7690"),
    "snow"   => ("#4A5F7C", "#93A9C4"),
    "storm"  => ("#2A2A44", "#5B5480"),
    "fog"    => ("#4A5568", "#94A3B8"),
    _        => ("#33465E", "#6B819B"),
};

SkiaGradient SkyGradient(string kind)
{
    var sky = Sky(kind);
    return new SkiaGradient
    {
        Type = GradientType.Linear,
        StartXRatio = 0, StartYRatio = 0, EndXRatio = 0.35f, EndYRatio = 1,
        Colors = new List<Color> { Color.Parse(sky.Top), Color.Parse(sky.Bottom) },
    };
}

SkiaLabel lblCity = null, lblWords = null, lblTemp = null, lblRange = null;
SkiaLabel lblFeels = null, lblWind = null, lblHum = null, lblNote = null, unitC = null, unitF = null;
SkiaShape unitPill = null, refreshChip = null, card = null;
SkiaSvg icon = null;
var hourLabels = new SkiaLabel[6];
var hourTemps = new SkiaLabel[6];
var hourIcons = new SkiaSvg[6];

bool fahrenheit = false, haveData = false;
double cNow = 0, cHi = 0, cLo = 0, cFeels = 0, wind = 0, humidity = 0;
int codeNow = 0;
var hours = new List<(string Hour, double C, int Code)>();

// +21°, -4°, 0°
string Deg(double celsius)
{
    var v = fahrenheit ? celsius * 9 / 5 + 32 : celsius;
    var r = Math.Round(v);
    return $"{(r > 0 ? "+" : "")}{r:0}°";
}

void Render()
{
    if (!haveData) return;

    var (kind, words) = Describe(codeNow);
    card.FillGradient = SkyGradient(kind);

    icon.SvgString = Icon(kind);
    icon.Opacity = 1;                     // placeholders start dimmed
    lblWords.Text = words;
    lblTemp.Text = Deg(cNow);
    lblRange.Text = $"H {Deg(cHi)}   L {Deg(cLo)}";
    lblFeels.Text = Deg(cFeels);
    lblWind.Text = $"{wind:0} km/h";
    lblHum.Text = $"{humidity:0}%";
    lblNote.Text = "Refresh";

    unitC.TextColor = fahrenheit ? Color.Parse("#99FFFFFF") : Colors.White;
    unitF.TextColor = fahrenheit ? Colors.White : Color.Parse("#99FFFFFF");

    for (int i = 0; i < 6 && i < hours.Count; i++)
    {
        hourLabels[i].Text = hours[i].Hour;
        hourTemps[i].Text = Deg(hours[i].C);
        hourIcons[i].SvgString = Icon(Describe(hours[i].Code).Kind);
        hourIcons[i].Opacity = 1;
    }

    card.Update();
}

void LoadWeather()
{
    lblNote.Text = "loading…";
    lblNote.Update();

    Fiddle.Load($"https://geocoding-api.open-meteo.com/v1/search?name={Uri.EscapeDataString(city)}&count=1&language=en", geo =>
    {
        if (!geo.TryGetProperty("results", out var results) || results.GetArrayLength() == 0)
        {
            lblNote.Text = $"unknown city: {city}";
            lblNote.Update();
            return;
        }

        var place = results[0];
        lblCity.Text = place.GetProperty("name").GetString();
        var lat = place.GetProperty("latitude").GetDouble();
        var lon = place.GetProperty("longitude").GetDouble();

        // Invariant() must wrap the interpolated part alone — concatenating first makes it a plain string
        var url = FormattableString.Invariant($"https://api.open-meteo.com/v1/forecast?latitude={lat}&longitude={lon}")
            + "&current=temperature_2m,apparent_temperature,relative_humidity_2m,wind_speed_10m,weather_code"
            + "&daily=temperature_2m_max,temperature_2m_min"
            + "&hourly=temperature_2m,weather_code&forecast_days=2&timezone=auto";

        Fiddle.Load(url, w =>
        {
            var cur = w.GetProperty("current");
            cNow = cur.GetProperty("temperature_2m").GetDouble();
            cFeels = cur.GetProperty("apparent_temperature").GetDouble();
            humidity = cur.GetProperty("relative_humidity_2m").GetDouble();
            wind = cur.GetProperty("wind_speed_10m").GetDouble();
            codeNow = cur.GetProperty("weather_code").GetInt32();

            var daily = w.GetProperty("daily");
            cHi = daily.GetProperty("temperature_2m_max")[0].GetDouble();
            cLo = daily.GetProperty("temperature_2m_min")[0].GetDouble();

            hours.Clear();
            var hourly = w.GetProperty("hourly");
            var times = hourly.GetProperty("time");
            var temps = hourly.GetProperty("temperature_2m");
            var codes = hourly.GetProperty("weather_code");
            var nowHour = cur.GetProperty("time").GetString();
            var start = 0;
            for (int i = 0; i < times.GetArrayLength(); i++)
                if (string.CompareOrdinal(times[i].GetString(), nowHour) > 0) { start = i; break; }
            for (int i = start; i < start + 6 && i < times.GetArrayLength(); i++)
                hours.Add((times[i].GetString().Substring(11, 2), temps[i].GetDouble(), codes[i].GetInt32()));

            haveData = true;
            Render();
        }, err => { lblNote.Text = "weather: " + err; lblNote.Update(); });
    }, err => { lblNote.Text = "geocoding: " + err; lblNote.Update(); });
}

// one hourly column — placeholders are the same shape as the real thing
SkiaControl HourCell(int i) => new SkiaLayout
{
    Type = LayoutType.Column,
    Spacing = 6,
    WidthRequest = 44,
    HorizontalOptions = LayoutOptions.Start,
    Children =
    {
        new SkiaLabel("--") { FontSize = 12, TextColor = Color.Parse("#B0FFFFFF"), HorizontalOptions = LayoutOptions.Center }.Assign(out hourLabels[i]),
        new SkiaSvg { SvgString = Icon("cloudy"), Opacity = 0.45, WidthRequest = 26, HeightRequest = 30, HorizontalOptions = LayoutOptions.Center }.Assign(out hourIcons[i]),
        new SkiaLabel("--°") { FontSize = 13, FontWeight = 600, TextColor = Colors.White, HorizontalOptions = LayoutOptions.Center }.Assign(out hourTemps[i]),
    }
};

SkiaControl Detail(string caption, out SkiaLabel value) => new SkiaLayout
{
    Type = LayoutType.Column,
    Spacing = 3,
    WidthRequest = 86,
    HorizontalOptions = LayoutOptions.Start,
    Children =
    {
        new SkiaLabel(caption) { FontSize = 11, TextColor = Color.Parse("#99FFFFFF"), HorizontalOptions = LayoutOptions.Center },
        new SkiaLabel("--") { FontSize = 16, FontWeight = 600, TextColor = Colors.White, HorizontalOptions = LayoutOptions.Center }.Assign(out value),
    }
};

card = new SkiaShape
{
    CornerRadius = 30,
    WidthRequest = 320,
    HeightRequest = 536,                 // fixed: the card never resizes when data arrives
    Padding = new Thickness(22, 20, 22, 16),
    HorizontalOptions = LayoutOptions.Center,
    VerticalOptions = LayoutOptions.Center,
    BackgroundColor = Color.Parse("#33465E"),
    FillGradient = SkyGradient("partly"), // something to look at while loading
    Children = new List<SkiaControl>
    {
        new SkiaLayout
        {
            Type = LayoutType.Column,
            Spacing = 0,
            HorizontalOptions = LayoutOptions.Fill,
            Children = new List<SkiaControl>
            {
                new SkiaLayer
                {
                    HeightRequest = 30,
                    HorizontalOptions = LayoutOptions.Fill,
                    Children = new List<SkiaControl>
                    {
                        new SkiaLabel(city)
                        {
                            FontSize = 21, FontFamily = "FontTextTitle", TextColor = Colors.White,
                            HorizontalOptions = LayoutOptions.Start, VerticalOptions = LayoutOptions.Center,
                        }.Assign(out lblCity),

                        new SkiaShape
                        {
                            CornerRadius = 13, WidthRequest = 74, HeightRequest = 26,
                            BackgroundColor = Color.Parse("#26FFFFFF"),
                            HorizontalOptions = LayoutOptions.End, VerticalOptions = LayoutOptions.Center,
                            Children = new List<SkiaControl>
                            {
                                new SkiaLabel("°C")
                                {
                                    FontSize = 13, FontWeight = 700, TextColor = Colors.White,
                                    HorizontalOptions = LayoutOptions.Start, VerticalOptions = LayoutOptions.Center,
                                    Margin = new Thickness(12, 0, 0, 0),
                                }.Assign(out unitC),

                                new SkiaLabel("°F")
                                {
                                    FontSize = 13, FontWeight = 700, TextColor = Color.Parse("#99FFFFFF"),
                                    HorizontalOptions = LayoutOptions.End, VerticalOptions = LayoutOptions.Center,
                                    Margin = new Thickness(0, 0, 12, 0),
                                }.Assign(out unitF),
                            }
                        }.Assign(out unitPill),
                    }
                },

                new SkiaSvg
                {
                    SvgString = Icon("cloudy"),
                    Opacity = 0.5,
                    WidthRequest = 104, HeightRequest = 118,
                    HorizontalOptions = LayoutOptions.Center,
                    Margin = new Thickness(0, 6, 0, 0),
                }.Assign(out icon),

                new SkiaLabel("—")
                {
                    FontSize = 15, TextColor = Color.Parse("#CCFFFFFF"),
                    HorizontalOptions = LayoutOptions.Center,
                }.Assign(out lblWords),

                new SkiaLabel("--°")
                {
                    FontSize = 64, FontFamily = "FontTextTitle", TextColor = Colors.White,
                    HorizontalOptions = LayoutOptions.Center, Margin = new Thickness(0, 2, 0, 0),
                }.Assign(out lblTemp),

                new SkiaLabel("H --°   L --°")
                {
                    FontSize = 14, TextColor = Color.Parse("#CCFFFFFF"),
                    HorizontalOptions = LayoutOptions.Center, Margin = new Thickness(0, 0, 0, 14),
                }.Assign(out lblRange),

                new SkiaShape
                {
                    CornerRadius = 18,
                    BackgroundColor = Color.Parse("#1FFFFFFF"),
                    HorizontalOptions = LayoutOptions.Fill,
                    Padding = new Thickness(6, 10),
                    Children = new List<SkiaControl>
                    {
                        new SkiaLayout
                        {
                            Type = LayoutType.Row,
                            Spacing = 0,
                            HorizontalOptions = LayoutOptions.Center,
                            Children = Enumerable.Range(0, 6).Select(i => HourCell(i)).ToList()
                        }
                    }
                },

                new SkiaLayout
                {
                    Type = LayoutType.Row,
                    Spacing = 0,
                    HorizontalOptions = LayoutOptions.Center,
                    Margin = new Thickness(0, 14, 0, 0),
                    Children = new List<SkiaControl>
                    {
                        Detail("FEELS LIKE", out lblFeels),
                        Detail("WIND", out lblWind),
                        Detail("HUMIDITY", out lblHum),
                    }
                },

                // refresh on its own chip: a card-wide tap handler would swallow the pill's taps
                new SkiaShape
                {
                    CornerRadius = 14, WidthRequest = 104, HeightRequest = 28,
                    BackgroundColor = Color.Parse("#26FFFFFF"),
                    HorizontalOptions = LayoutOptions.Center,
                    Margin = new Thickness(0, 14, 0, 0),
                    Children = new List<SkiaControl>
                    {
                        new SkiaLabel("loading…")
                        {
                            FontSize = 12, FontWeight = 600, TextColor = Colors.White,
                        }.Center().Assign(out lblNote),
                    }
                }.Assign(out refreshChip),
            }
        }
    }
};

unitPill.OnTapped(me => { fahrenheit = !fahrenheit; Render(); });
refreshChip.OnTapped(me => { Fiddle.Emit("refresh", new { city }); LoadWeather(); });

LoadWeather();
return card;