# iOS Weather

> Gradient skies, hourly forecast and 10-day list with a day/night toggle.

- Author: Atelier UI
- License: MIT
- Page: https://chromotion.dev/en/templates/ios-weather
- Site: Atelier UI (https://chromotion.dev)

## Install

```bash
npx shadcn@latest add https://chromotion.dev/r/ios-weather.json
```

Adds the page at `app/ios-weather/page.tsx` together with the components below. Stack: React + TypeScript + Tailwind CSS v4, shadcn/ui conventions. `cn` comes from `@/lib/utils`.

## Components

- [iOS Kit](https://chromotion.dev/r/ios-kit.md): `@/components/ios-kit`

## Dependencies

```bash
npm install lucide-react
```

## Source

```tsx title="app/ios-weather/page.tsx"
"use client"

import * as React from "react"
import { Cloud, CloudRain, CloudSun, Droplets, List, MapPin, Moon, Sun, Sunrise, Thermometer, Wind } from "lucide-react"

import { cn } from "@/lib/utils"
import { IOSDevice } from "@/components/ios-kit"

type Icon = React.ComponentType<{ className?: string }>

const hours: { time: string; icon: Icon; temp: number }[] = [
  { time: "Now", icon: CloudSun, temp: 18 },
  { time: "11 AM", icon: CloudSun, temp: 19 },
  { time: "12 PM", icon: Sun, temp: 21 },
  { time: "1 PM", icon: Sun, temp: 21 },
  { time: "2 PM", icon: CloudSun, temp: 20 },
  { time: "3 PM", icon: Cloud, temp: 19 },
  { time: "4 PM", icon: CloudRain, temp: 17 },
  { time: "5 PM", icon: CloudRain, temp: 16 },
]

const days: { day: string; icon: Icon; low: number; high: number }[] = [
  { day: "Today", icon: CloudSun, low: 13, high: 21 },
  { day: "Fri", icon: CloudRain, low: 12, high: 17 },
  { day: "Sat", icon: CloudRain, low: 11, high: 16 },
  { day: "Sun", icon: Cloud, low: 11, high: 18 },
  { day: "Mon", icon: Sun, low: 12, high: 22 },
  { day: "Tue", icon: Sun, low: 13, high: 24 },
  { day: "Wed", icon: CloudSun, low: 14, high: 23 },
]

const stats: { label: string; value: string; note: string; icon: Icon }[] = [
  { label: "UV Index", value: "4", note: "Moderate", icon: Sun },
  { label: "Wind", value: "12 mph", note: "Gusts to 20", icon: Wind },
  { label: "Sunrise", value: "6:48 AM", note: "Sunset 4:52 PM", icon: Sunrise },
  { label: "Humidity", value: "64%", note: "Dew point 11°", icon: Droplets },
]

const MIN = 10
const MAX = 26

function Glass({ title, icon: Icon, className, children }: { title: string; icon: Icon; className?: string; children: React.ReactNode }) {
  return (
    <section className={cn("rounded-[22px] bg-white/15 p-3.5 backdrop-blur-xl", className)}>
      <h2 className="mb-2 flex items-center gap-1.5 text-[12px] font-medium tracking-wide text-white/65 uppercase">
        <Icon className="size-3.5" />
        {title}
      </h2>
      {children}
    </section>
  )
}

export default function IOSWeather() {
  const [night, setNight] = React.useState(false)

  return (
    <IOSDevice light className="bg-black">
      <div
        aria-hidden
        className={cn(
          "absolute inset-0 transition-opacity duration-700",
          "bg-gradient-to-b from-[#2f80ed] via-[#56a8f5] to-[#9fd1f7]"
        )}
      />
      <div
        aria-hidden
        className={cn(
          "absolute inset-0 bg-gradient-to-b from-[#0b1026] via-[#1b2552] to-[#3a3f7a] transition-opacity duration-700",
          night ? "opacity-100" : "opacity-0"
        )}
      />

      <div role="region" aria-label="Weather forecast" tabIndex={0} className="relative min-h-0 flex-1 overflow-y-auto px-4 pt-[60px] pb-[90px] text-white focus-visible:ring-2 focus-visible:ring-white/60 focus-visible:outline-none focus-visible:ring-inset [scrollbar-width:none] [&::-webkit-scrollbar]:hidden">
        <header className="flex flex-col items-center pt-2 text-center">
          <h1 className="text-[34px] leading-10 font-normal">San Francisco</h1>
          <p className="text-[96px] leading-[100px] font-thin">{night ? "14°" : "18°"}</p>
          <p className="text-[20px] font-medium text-white/90">{night ? "Clear" : "Partly Cloudy"}</p>
          <p className="text-[20px] font-medium text-white/90">H:21°  L:13°</p>
        </header>

        <div className="mt-6 flex flex-col gap-3">
          <Glass title="Hourly forecast" icon={CloudSun}>
            <ul tabIndex={0} aria-label="Hourly forecast" className="-mx-1 flex gap-5 overflow-x-auto rounded-md px-1 pb-1 outline-none focus-visible:ring-2 focus-visible:ring-white/70 [scrollbar-width:none]">
              {hours.map(({ time, icon: Icon, temp }) => (
                <li key={time} className="flex shrink-0 flex-col items-center gap-2 text-[15px]">
                  <span className="font-medium whitespace-nowrap">{time}</span>
                  <Icon className="size-6" />
                  <span className="text-[18px] font-medium">{temp}°</span>
                </li>
              ))}
            </ul>
          </Glass>

          <Glass title="7-day forecast" icon={List}>
            <ul className="divide-y divide-white/20">
              {days.map(({ day, icon: Icon, low, high }) => (
                <li key={day} className="flex items-center gap-3 py-2 text-[18px]">
                  <span className="w-12 font-medium">{day}</span>
                  <Icon className="size-6" />
                  <span className="w-8 text-right text-white/60">{low}°</span>
                  <span className="relative h-1 flex-1 rounded-full bg-black/20">
                    <span
                      className="absolute inset-y-0 rounded-full bg-gradient-to-r from-cyan-300 via-yellow-300 to-orange-400"
                      style={{ left: `${((low - MIN) / (MAX - MIN)) * 100}%`, right: `${100 - ((high - MIN) / (MAX - MIN)) * 100}%` }}
                    />
                  </span>
                  <span className="w-8 font-medium">{high}°</span>
                </li>
              ))}
            </ul>
          </Glass>

          <div className="grid grid-cols-2 gap-3">
            {stats.map(({ label, value, note, icon }) => (
              <Glass key={label} title={label} icon={icon} className="aspect-square">
                <p className="text-[32px] leading-9 font-medium">{value}</p>
                <p className="mt-1 text-[14px] text-white/80">{note}</p>
              </Glass>
            ))}
          </div>
          <Glass title="Feels like" icon={Thermometer}>
            <p className="text-[32px] leading-9 font-medium">{night ? "12°" : "17°"}</p>
            <p className="mt-1 text-[14px] text-white/80">Wind is making it feel cooler.</p>
          </Glass>
        </div>
      </div>

      <div className="absolute inset-x-0 bottom-0 z-10 flex h-[83px] items-start justify-between border-t border-white/20 bg-black/20 px-6 pt-3 backdrop-blur-xl">
        <button type="button" aria-label="Map" className="p-1"><MapPin className="size-6" /></button>
        <button type="button" aria-label={night ? "Show daytime" : "Show nighttime"} onClick={() => setNight((value) => !value)} className="rounded-full bg-white/20 p-2 active:scale-90">
          {night ? <Sun className="size-5" /> : <Moon className="size-5" />}
        </button>
        <button type="button" aria-label="Locations" className="p-1"><List className="size-6" /></button>
      </div>
    </IOSDevice>
  )
}
```
