Skip to content

2 cases built from this template

Same prompt, different [VARIABLES]. Click one to preview it above.

About this template

Wattline is a dark analytics dashboard template for a home energy-monitoring app, built with Vite, React, TypeScript and Tailwind CSS. It is a single static page with a fixed sidebar, a sticky top bar with a Day / Week / Month / Year switcher, four KPI tiles, a consumption-versus-solar line chart, a live load gauge, a breakdown by room or by hour, a device list with working toggles, a time-of-use tariff strip and an activity feed. Every chart is hand-written SVG, so you can change a curve, color or animation without working around a chart library.

It suits founders and developers prototyping a smart-home, solar or building-management product, agencies pitching an IoT client, and anyone who wants a realistic data screen for studying React state and SVG animation. All numbers come from one data file with four fixed data sets. Nothing uses the system clock or random values, so the screen looks the same on every load and works as a dependable demo.

The look is near-black blue-grey panels with thin borders. One lime accent marks good news and active data, and amber marks peaks and warnings. Numbers use tabular figures in Space Grotesk and JetBrains Mono, so they don't jitter while counting. When you change the range, the line chart morphs into its new shape, the KPI values count to their new totals and the bars grow back in one after another. Flipping a device switch updates the gauge, the live pill and the sidebar badge at once, and the gauge turns amber when the load goes over the peak limit. All motion stops when reduced motion is turned on.

To adapt it, change the five variables for brand, site, operator, currency and focus, then rewrite the device names, rooms, tips and activity items in the content file. A bakery kitchen, a small office or a greenhouse fits the same layout with no visual changes.

The template prompt

The live demo was built from exactly this text — nothing added. The other cases only change the values of the [VARIABLES].

# Wattline — Dark Energy Monitoring Dashboard with Animated SVG Charts

Build this website exactly as described below. Replace the [VARIABLES] with your own values; keep everything else.

## Brief
[BRAND] is a dark, single-screen analytics dashboard for [FOCUS] at [SITE_NAME], run by [OPERATOR_NAME]. It is an app shell with a fixed left sidebar, a sticky top bar holding a Day / Week / Month / Year range switcher, four KPI tiles with count-up numbers and sparklines, a hero consumption-vs-solar SVG line/area chart that morphs between data sets, a live-load SVG gauge, an SVG bar chart (by room or by hour) with staggered grow-in, a device list with real toggle switches that update the live totals, a time-of-use tariff strip and an activity timeline. All data is fake, deterministic and kept in one data file, and a single range state swaps every view. Default copy is written for a family home with solar panels and a heat pump. When the variable values differ, rewrite every piece of copy (greeting, site details, device names, rooms, tips, activity items) so it fits the new site, and keep the same structure, counts and number formats.

- Audience: People who run a home or small site with solar, a heat pump or heavy appliances and want to see in seconds where energy goes, what it costs and what is running right now. Builders use it as a starting screen for IoT, energy or building-management products.
- Goal: Make energy use readable at a glance and feel live: change the time range, hover a chart, flip a device off and watch the totals react. The screen should show careful, controlled SVG animation and a responsive app shell that works from 1440px desktop down to a 390px phone.
- Tone: Calm, precise, quietly technical. Short labels, sentence case, numbers first. Friendly but never cute. No exclamation marks.

## Stack
- Framework: Vite 5 + React 18 + TypeScript
- Styling: Tailwind CSS 3.4
- Libraries: react (MIT), react-dom (MIT), lucide-react (ISC), @fontsource-variable/inter (OFL-1.1), @fontsource-variable/space-grotesk (OFL-1.1), @fontsource/jetbrains-mono (OFL-1.1), tailwindcss (dev) (MIT), postcss (dev) (MIT), autoprefixer (dev) (MIT), vite + @vitejs/plugin-react (dev) (MIT), typescript (dev) (Apache-2.0)
- Build: `npm run build` → static files in `dist/`. No backend, no server code, no external APIs.
- Setup:
  - Scaffold with npm create vite@latest . -- --template react-ts, then run npm i lucide-react @fontsource-variable/inter @fontsource-variable/space-grotesk @fontsource/jetbrains-mono and npm i -D [email protected] postcss autoprefixer, then npx tailwindcss init -p.
  - package.json scripts: dev = vite, build = tsc -b && vite build, preview = vite preview.
  - vite.config.ts: plugins [react()], base: './', build: { outDir: 'dist', sourcemap: false, emptyOutDir: true }.
  - tailwind.config.js: content ['./index.html','./src/**/*.{ts,tsx}']. Extend colors with ink #0B0F14, panel #11161D, raised #161D26, line #1F2833, line-strong #2A3542, text #E6EDF3, muted #8B98A8, faint #5A6675, lime #C6FF3D, lime-deep #8FBF1F, amber #FFB547. Extend fontFamily with display ['Space Grotesk Variable','system-ui','sans-serif'], sans ['Inter Variable','system-ui','sans-serif'], mono ['JetBrains Mono','ui-monospace','monospace'].
  - Also in tailwind.config.js, define keyframes and animation utilities: pulse-ring (box-shadow 0 0 0 0 rgba(198,255,61,0.55) to 0 0 0 8px rgba(198,255,61,0) over 2s infinite), fade-up (opacity 0 + translateY(12px) to opacity 1 + none, 450ms cubic-bezier(0.22,1,0.36,1) both) and toast-in (translateY(16px) + opacity 0 to none, 260ms).
  - src/main.tsx imports '@fontsource-variable/inter', '@fontsource-variable/space-grotesk', '@fontsource/jetbrains-mono/400.css', '@fontsource/jetbrains-mono/500.css' and './index.css'.
  - src/index.css: @tailwind base/components/utilities. html { background:#0B0F14; color:#E6EDF3; color-scheme: dark; scroll-behavior: smooth } body font-sans 14px antialiased. Add .num { font-variant-numeric: tabular-nums; } and a global :focus-visible outline of 2px #C6FF3D with 2px offset. Inside @media (prefers-reduced-motion: reduce), set animation: none, transition-duration: 0ms and scroll-behavior: auto on everything.
  - File layout: src/content.ts (every string and the variable values), src/data/energy.ts (data sets, devices, tariff, activity), src/lib/chart.ts (niceTicks, Fritsch–Carlson monotone cubic path builder, linear resample, formatters), src/hooks/ (useTween, useCountUp, useReducedMotion, useElementSize via ResizeObserver, useInView via IntersectionObserver), src/components/ (Sidebar, TopBar, RangeSwitcher, KpiTile, Sparkline, UsageChart, LiveGauge, BreakdownChart, DeviceList, Switch, TariffStrip, ActivityFeed, SettingsDrawer, MoreSheet, MobileTabBar, Toast).
  - State: App.tsx holds one useReducer with range ('day'|'week'|'month'|'year', default 'week'), devices on/off map, breakdownMode ('rooms'|'hours', default 'rooms'), settings {animate, showSolar, showCost, peakGuard} and a liveTick counter. Every derived number (totals, cost, coverage, live load) comes from useMemo selectors and is never hard-coded in components.
  - All animation goes through hand-written requestAnimationFrame hooks (useTween with easeInOutCubic and easeOutCubic). When the user prefers reduced motion or settings.animate is off, every duration is 0 and values jump.
  - Data (src/data/energy.ts), DAY: labels '00' to '23', title 'Today, 27 Oct'. used kWh = [0.42,0.38,0.35,0.34,0.36,0.48,0.92,1.46,1.21,0.74,0.62,0.58,0.66,0.61,0.57,0.69,0.98,1.62,2.14,1.88,1.41,1.02,0.71,0.52]. solar = [0,0,0,0,0,0,0.04,0.18,0.41,0.72,0.98,1.16,1.22,1.14,0.96,0.70,0.42,0.16,0.03,0,0,0,0,0]. peak 3.8 kW at '18:12'. Deltas: used −6.2%, cost −9.8%, peak +4.1%, solar +3.0 pts. Caption 'vs. yesterday'.
  - Data, WEEK: labels Wed, Thu, Fri, Sat, Sun, Mon, Tue with tooltip dates '21 Oct' to '27 Oct', title '21 – 27 Oct'. used = [18.9,20.6,22.3,27.1,25.4,19.8,21.4]. solar = [8.4,4.1,7.7,9.0,8.6,7.9,6.2]. peak 4.6 kW at 'Sat 24 Oct, 19:12'. Deltas: used +2.4%, cost −1.7%, peak −3.5%, solar +1.2 pts. Caption 'vs. previous week'.
  - Data, MONTH: 30 points for 28 Sep to 27 Oct, labels 'D Mon' (e.g. '28 Sep'), title '28 Sep – 27 Oct'. For i = 0..29, used[i] = round1(19.5 + 3.2*sin(i*0.85) + (i%7===5 || i%7===6 ? 4.8 : 0)) and solar[i] = round1(6.8 + 2.4*sin(i*0.55 + 1.2)). Peak 5.1 kW at 'Sun 18 Oct, 18:40'. Deltas: used −4.8%, cost −7.3%, peak +2.0%, solar −0.8 pts. Caption 'vs. previous 30 days'.
  - Data, YEAR: labels Nov, Dec, Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, title 'Nov 2025 – Oct 2026'. used = [649,731,742,668,610,521,468,431,452,447,489,562]. solar = [92,71,84,121,198,262,318,341,336,298,231,152]. peak 6.4 kW at '14 Jan, 17:55'. Deltas: used −11.6%, cost −14.2%, peak −5.1%, solar +6.4 pts. Caption 'vs. previous 12 months'.
  - Derived metrics: Energy used = Σused. Grid cost = Σ max(0, used−solar) × 0.28 (blended rate, shown as [CURRENCY_SYMBOL]). Solar coverage = Σsolar / Σused × 100. Peak load comes from the data set. Formatting: kWh with 1 decimal under 1,000, otherwise 0 decimals with thousands separators. Cost with 2 decimals. Percent with 1 decimal.
  - Room shares in % (Kitchen, Heat pump, Living room, Laundry, Office, Bedrooms): day [31,12,21,9,15,12], week [28,16,19,14,12,11], month [27,19,18,13,12,11], year [25,24,16,13,12,10]. Room kWh = share/100 × Σused. Hours view = the DAY used array × factor (day 1, week 1.07, month 1.02, year 0.89).
  - Devices (name · room · ratedW · default on · kWh today · Lucide icon): Heat pump · Utility room · 1240 · on · 6.8 · Thermometer. Fridge-freezer · Kitchen · 86 · on · 1.1 · Refrigerator. Washing machine · Laundry · 412 · on · 0.9 · WashingMachine. Office desk · Office · 148 · on · 0.8 · Monitor. Living room lights · Living room · 64 · on · 0.4 · Lamp. TV & media · Living room · 120 · off · 0.6 · Tv. Oven · Kitchen · 2100 · off · 1.2 · CookingPot. EV charger · Driveway · 7200 · off · 0.0 · PlugZap, with note 'Scheduled 00:30'. Base load is 90 W, always on.
  - Live load = 90 W + Σ ratedW × jitter for devices that are on. Every 2000 ms liveTick increments, and each on device multiplies its ratedW by the cycle [1, 1.02, 0.99, 1.03, 0.98, 1.01][(tick + deviceIndex) % 6]. At tick 0 the default live load is 2.04 kW. Solar now is a fixed 0.00 kW. From grid = live load. Now is fixed at 23:52 on Tue 27 Oct 2026. Never read the system clock and never use Math.random.
  - Tariff bands (hour ranges, rate per kWh): 00–07 Off-peak 0.19, 07–16 Standard 0.28, 16–20 Peak 0.41, 20–23 Standard 0.28, 23–24 Off-peak 0.19.
  - index.html: <title>[BRAND] — [FOCUS] dashboard</title> and <link rel="icon" href="./favicon.svg" type="image/svg+xml">. public/favicon.svg is the logo mark described in the App shell section.
  - Write ASSETS.md at the project root. List Inter, Space Grotesk and JetBrains Mono (Fontsource packages, OFL-1.1, with their npm URLs) and Lucide icons (lucide-react, ISC), and state that every other graphic is drawn in code.

## Variables
- [BRAND] — Brand name (default: "Gridlark"). Invented app name. Shown in the sidebar wordmark, the title and the footer strip.
- [SITE_NAME] — Monitored site (default: "Juniper Lane house"). The home, shop or building being monitored. Shown in the site switcher and the top-bar subline.
- [OPERATOR_NAME] — Signed-in person (default: "Noor Halloran"). Invented full name. Its first name goes in the greeting and its initials in the code-drawn avatar.
- [CURRENCY_SYMBOL] — Currency symbol (default: "€"). Prefix for every cost and tariff value.
- [FOCUS] — What is monitored (default: "Home energy"). Short noun phrase, e.g. 'Home energy' or 'Kitchen energy'. Used in the top-bar subline and the page title.
- [CREDIT] — keep a small "Template by VibeFather" link to https://vibefather.ai/t/wattline in the footer (default: yes; delete this line to remove it)

The copy under Pages & Sections is written for the default values. If your values differ, rewrite every headline, paragraph, label and image for them — same length, tone and structure. Layout, colors, type and motion stay as described.
Keep all copy, image paths and links in one content file (for example `src/content.ts`), keyed by section, so the content can be swapped later without touching components.

## Design
### Colors
- Ink `#0B0F14` — App background, sidebar background, text on lime
- Panel `#11161D` — Card surfaces, segmented-control track
- Raised `#161D26` — Active nav item, icon tiles, tooltips, hover rows, drawers
- Line `#1F2833` — 1px borders, dividers, chart gridlines, gauge track
- Line strong `#2A3542` — Hover borders, inactive bars, switch-off track, crosshair
- Text `#E6EDF3` — Primary text and numbers
- Muted `#8B98A8` — Secondary text, nav labels, solar line
- Faint `#5A6675` — Axis labels, captions, section labels, off-state icons
- Lime `#C6FF3D` — The single accent: active range pill, usage line, on-state switches, good deltas, focus ring, live dot
- Lime deep `#8FBF1F` — Start of the gauge arc gradient (to Lime)
- Amber `#FFB547` — Peaks, peak-tariff band, bad deltas, peak-guard warning, notification dot
- Lime glow `rgba(198,255,61,0.10)` — Radial glow behind charts, good-delta chip background, off-peak band at 0.14
- Amber glow `rgba(255,181,71,0.12)` — Bad-delta chip background, peak band at 0.16

### Typography
- Display: greeting, brand wordmark, KPI and gauge numbers, card titles on mobile: Space Grotesk Variable (@fontsource-variable/space-grotesk) (OFL), weights 500/600
- UI text: nav, labels, body, buttons: Inter Variable (@fontsource-variable/inter) (OFL), weights 400/500/600
- Data: axis labels, timestamps, wattage readouts, tooltip values, chips: JetBrains Mono (@fontsource/jetbrains-mono) (OFL), weights 400/500

### Layout & motion
- Spacing: 4px base unit. Desktop main area: padding 28px top, 32px sides, 40px bottom, max-width 1320px. 12-column grid with a 20px gap. Card padding 20px, card header to content gap 16px. Mobile (<768px): padding 16px, grid gap 12px, card padding 16px, and 88px bottom padding to clear the tab bar. All numbers use tabular figures (.num).
- Radius: Cards 16px. Buttons, inputs, icon tiles and nav items 10px. Segmented controls 12px outer, 9px inner pill. Chips and switches fully rounded. Bars 6px top corners (rooms) and 3px (hours). Tooltips 10px.
- Motion: UI easing cubic-bezier(0.22,1,0.36,1). Data tweens use easeInOutCubic (morphs) or easeOutCubic (count-ups and grow-ins), driven by requestAnimationFrame.
- Motion: On load, cards fade up 12px over 450ms, staggered 60ms in DOM order (KPI tiles first).
- Motion: KPI values count up from the previous value to the new one over 900ms easeOutCubic. Delta chips cross-fade over 200ms.
- Motion: Usage chart: on first view the usage line draws with stroke-dashoffset from full length to 0 over 1200ms, and the area fills from opacity 0 to 1 over 600ms after a 300ms delay. On range change, both series and the y-scale max morph over 700ms, and x labels fade out over 150ms and back in over 200ms.
- Motion: Bars grow from scaleY(0) with transform-origin at the bottom over 600ms easeOutCubic, staggered 40ms, the first time the card is 30% in view. On range change their heights tween over 500ms. On mode switch the chart cross-fades over 150ms and the bars grow in again.
- Motion: The range switcher pill slides with translateX over 320ms. The switch knob slides 16px over 180ms. The gauge arc and center number tween over 600ms. Device wattage counts to its new value over 400ms, and the toggled row flashes rgba(198,255,61,0.06) for 600ms.
- Motion: The live dot (top-bar pill and tariff now-marker) loops the pulse-ring animation every 2s.
- Motion: Drawers slide in over 280ms with a 200ms overlay fade. Toasts use toast-in and auto-dismiss after 3000ms.
- Motion: prefers-reduced-motion: no draw-ins, morphs, staggers, pulses or slides. Values and positions change instantly, and the live numbers still update every 2s without tweening.
- Imagery: No photos or bitmaps. Everything visual is SVG or CSS drawn in code: charts, sparklines, gauge, tariff strip, the logo mark, and initials avatars on Raised circles. Lucide icons are used at 16–18px with a 1.75 stroke. Depth comes only from 1px borders, soft radial lime glows behind the chart cards (radial-gradient(600px circle at 30% 0%, rgba(198,255,61,0.08), transparent 60%)) and drop-shadow glows on active data points. Tooltips and drawers use the shadow 0 8px 24px rgba(0,0,0,0.4). No light theme.

## Pages & Sections
### Overview (`/index.html`)
1. **App shell and sidebar** — Logo mark: a 28×28 SVG rounded square (rx 8) filled #C6FF3D, with a 2px #0B0F14 pulse polyline through points (5,15) (10,15) (12,9) (16,19) (18,13) (23,13). Next to it, the [BRAND] wordmark in Space Grotesk 600 18px. Below that is a site switcher button with a lime House icon, '[SITE_NAME]' (13px Inter 500) over '3 bed · 6.2 kWp solar' (12px muted) and a ChevronsUpDown icon. Clicking it opens a small popover that lists [SITE_NAME] with a lime Check and a disabled 'Add a site' row. Nav group label 'Monitor' (11px uppercase, tracking 0.08em, Faint) holds Overview (LayoutGrid), Usage (LineChart), Rooms (BarChart3) and Devices (Plug, with a mono 11px count badge '5/8' that updates live). Group 'Account' holds Tariffs (Receipt) and Settings (Settings). Pinned to the bottom is a goal card: 'October goal' (12px muted), '512 / 600 kWh' (mono 13px), a 6px progress bar (Line track, Lime fill at 85%) and the caption '88 kWh left · 4 days to go'. Last is a user row: a 36px Raised circle with a Line-strong border and the initials of [OPERATOR_NAME] ('NH') in Space Grotesk 600 13px Lime, the name (13px 500), 'Household admin' (12px muted) and a MoreHorizontal icon button that opens Settings.
   Layout: ≥1024px: CSS grid grid-cols-[248px_1fr]. The sidebar is sticky top-0 h-screen, Ink background, 1px right border Line, padding 20px 16px, flex column with 24px gaps and the goal card pushed down with mt-auto. Nav items are 40px tall, radius 10, padding 0 12px, 14px Inter 500 Muted, 18px icons, 12px gap. The active item has a Raised background, Text color, a Lime icon and a 2px × 18px lime bar at the left edge. Hover gives a Raised background. Scroll-spy (IntersectionObserver on the section ids overview, usage, rooms, devices, tariffs) sets the active item. Clicking scrolls to the matching section id, and Settings opens the drawer. 768–1023px: a 72px icon rail with logo mark only, icons centered and labels shown as tooltips on hover or focus. The site card, goal card and user text are hidden, and only the avatar is shown. <768px: the sidebar is hidden and replaced by the mobile tab bar (see Overlays).
   Motion: Active bar and background cross-fade over 200ms. The Devices badge number counts on change. The popover fades and scales from 0.97 over 160ms.
2. **Top bar and range switcher** — H1 'Good evening, Noor' (the first name of [OPERATOR_NAME]) in Space Grotesk 600 24px, tracking −0.01em. Subline '[SITE_NAME] · [FOCUS] overview' in 13px Muted. The right cluster, in order: (1) segmented range switcher 'Day' 'Week' 'Month' 'Year' (default Week), with title attribute 'Shortcut: D · W · M · Y'; (2) date chip with a Calendar icon and the active data set title in mono 12px (e.g. '21 – 27 Oct'); (3) live pill with an 8px lime dot (pulse-ring) and 'Live · 2.04 kW' in mono 12px, updating every 2s; (4) a 36×36 bell icon button with a 6px Amber dot. The bell opens a 300px popover with two alerts: 'Peak load hit 3.8 kW at 18:12' (amber TriangleAlert icon) and 'EV charger scheduled for 00:30 off-peak' (lime Clock icon). Keys D, W, M and Y switch range unless focus is inside a text input.
   Layout: Desktop: sticky top-0 z-30, height 72px, background rgba(11,15,20,0.78) with backdrop-blur 12px, 1px bottom border Line, padding 0 32px, flex with space-between and centered items. The right cluster gap is 12px. Switcher: Panel track, 1px Line border, radius 12, padding 4px, buttons 36px tall × 64px wide at 13px Inter 600 Muted. An absolutely positioned 9px-radius pill in Lime sits under the active button, whose text turns Ink. Buttons use role=radiogroup/radio with aria-checked, and arrow keys move between them. Chips are 36px tall, radius 10, 1px Line border, Panel background, padding 0 12px. Mobile (<768px), three rows: row 1 (not sticky) is the logo mark, [BRAND] wordmark, live pill on the right and the 32px avatar. Row 2 is the H1 at 20px, then the subline, then the date title in mono 12 Faint. Row 3 is the switcher, sticky top-0 with a blurred Ink background, full width as a 4-column grid of equal buttons. The date chip and bell are hidden on mobile.
   Motion: The pill translates to the active button over 320ms. The whole dashboard re-derives from the new range, and each module animates as described in its own section.
3. **KPI tiles** — id='overview'. Four tiles. Each has a 32px Raised icon tile with a Lime Lucide icon, a 13px Muted label, a delta chip at the top right, a big value with its unit, a 12px Faint caption and a sparkline of the active range. (1) 'Energy used', Zap, value Σused + ' kWh', caption = the data set caption (e.g. 'vs. previous week'), lime sparkline of used. (2) 'Grid cost', Wallet, value '[CURRENCY_SYMBOL]' + cost, caption 'Blended rate [CURRENCY_SYMBOL]0.28/kWh', lime sparkline of per-point cost. (3) 'Peak load', Activity, value + ' kW', caption = peak time string (e.g. 'Sat 24 Oct, 19:12'), Amber sparkline of used with the max point marked by a 3px amber dot. (4) 'Solar coverage', Sun, value + '%', caption = Σsolar + ' kWh generated', lime sparkline of solar. Delta chips: for used, cost and peak, a negative change is good (lime text on Lime glow, ArrowDownRight 12px) and a positive change is bad (amber text on Amber glow, ArrowUpRight). For solar, positive is good. Chip text is mono 11px, e.g. '−6.2%' or '+1.2 pts'.
   Layout: Grid: lg 4 tiles at col-span-3 each, md 2 per row, mobile 2×2. Tile: card (Panel, 1px Line, radius 16, padding 20) with min-height 156px. The top row is flex with the icon tile and label on the left and the chip on the right. The value row sits 16px below with the value in Space Grotesk 600 32px (24px on mobile), .num, and the unit in 14px Muted with a 4px gap. Desktop: the caption is bottom-left and the sparkline (112×36 SVG) bottom-right. Mobile: the sparkline goes full width at 28px high below the caption. Sparkline: monotone path, 1.5px stroke, area gradient from stroke color at 0.18 to 0, a 3px end dot with drop-shadow 0 0 6px of the stroke color. Hover: border Line-strong and translateY(−2px) over 200ms.
   Motion: Values count up over 900ms on load and on every range change, starting from the previously shown value. Sparklines morph with the same 700ms tween as the main chart. Tiles fade up in the 60ms stagger.
4. **Usage chart (hero)** — id='usage'. Title 'Consumption vs. solar' (14px Inter 600). The subline depends on range: 'Hourly, today', 'Daily, last 7 days', 'Daily, last 30 days' or 'Monthly, last 12 months'. On the right: a legend with a lime 10×2 swatch 'Used' and a dashed muted swatch 'Solar' (hidden when settings.showSolar is off), plus a total chip in mono 12 such as '155.5 kWh'. Chart: usage drawn as a 2px Lime monotone-cubic line with a vertical area gradient from Lime at 0.28 to 0. Solar drawn as a 1.5px Muted dashed line (dasharray 4 4) with no fill. The peak point gets a 4px Amber dot with a 10px amber halo at 0.25 opacity and a mono 10px Amber 'Peak' label 10px above. Tooltip (168px, Raised, 1px Line-strong, radius 10, padding 10px, shadow) shows the point label in mono 11 Muted (hour, '24 Oct', '18 Oct' or month), then rows 'Used 27.1 kWh', 'Solar 9.0 kWh' and 'Grid cost [CURRENCY_SYMBOL]5.07'. The cost row is hidden when settings.showCost is off.
   Layout: Card at lg col-span-8, full width below lg. Card background: Panel plus the radial lime glow. The SVG is measured with ResizeObserver and drawn in pixel coordinates (no stretched viewBox). Height 288px desktop, 220px mobile. Inner padding: left 40 for y labels, bottom 26 for x labels, top 16, right 8. Y axis: 4 niceTicks from 0 to (max × 1.15) rounded up to a nice step, dashed (2 4) Line gridlines across, labels mono 11 Faint at the left. X labels mono 11 Faint: Day every 3rd hour, Week all 7, Month every 5th, Year all 12 on desktop and every 2nd on mobile. Hover or touch: snap to the nearest index and show a 1px Line-strong crosshair, a 5px Lime dot with drop-shadow 0 0 12px rgba(198,255,61,0.6) on usage and a 4px Muted dot on solar. The tooltip sits 12px right of the crosshair and flips left when it would overflow. The SVG has tabIndex 0, role='img' and an aria-label summarising the totals. Left and Right arrows move the crosshair, and Escape hides it.
   Motion: First view: draw-in through stroke-dashoffset (1200ms), then the area fades in. Range change: resample old and new series to 96 evenly spaced samples by linear interpolation, tween the samples and the y-scale max over 700ms easeInOutCubic, draw intermediate frames from the samples, then swap to the exact monotone path of the new series. The y labels follow the tweened scale. The crosshair moves without easing.
5. **Live load gauge** — Title 'Live load', subline 'Updated every 2 s'. Center: live load in Space Grotesk 600 40px .num ('2.04') with 'kW' in 14px Muted, and under it '5 of 8 devices on' (12px Muted). Below the gauge is a 3-column mini-stat row separated by 1px Line dividers. Each stat has a label in 11px Faint and a value in mono 13: 'From grid' 2.04 kW, 'Solar now' 0.00 kW, 'Rate now' [CURRENCY_SYMBOL]0.19/kWh with a small Lime 'Off-peak' tag under it. When live load ≥ 5.0 kW (and settings.peakGuard is on): the arc and center unit turn Amber, the card border becomes rgba(255,181,71,0.4), and an amber chip 'Above 5 kW peak guard' appears in the header.
   Layout: Card at lg col-span-4 (beside the usage chart, same height), full width below lg. SVG viewBox 0 0 220 180 centered with max-width 260px. The arc is centered at (110,110) with r 84, sweeping 240° from 210° to −30° in stroke 14 with round caps. The track is Line, and the value arc uses a linear gradient from Lime deep to Lime. The scale is 0–10 kW, with 11 small 1px Line-strong ticks just outside the arc and mono 10 Faint labels '0', '5' and '10'. At the end of the value arc sits a 6px Ink-centered lime dot with a lime drop-shadow glow.
   Motion: Arc length and center number tween over 600ms on every live tick and device toggle. The amber state cross-fades over 200ms.
6. **Breakdown bar chart** — id='rooms'. Title 'Where it went'. Subline 'Share of energy by room' in Rooms mode or 'Average day by hour' in Hours mode. Header right: a small segmented toggle 'Rooms | Hours'. Rooms mode: 6 bars sorted by value descending, each with a mono 12 value label above ('43.5 kWh'), the room name below in 12px Inter Muted and the share in mono 11 Faint ('28%'). The tallest bar is Lime with drop-shadow 0 0 16px rgba(198,255,61,0.35). The others are Line-strong and turn rgba(198,255,61,0.55) on hover. Tooltip: 'Kitchen · 28% · 43.5 kWh · [CURRENCY_SYMBOL]12.18'. Hours mode: 24 bars, colored Amber during hours 16–19 (peak), rgba(198,255,61,0.45) during hours 23 and 0–6 (off-peak) and Line-strong otherwise. X labels every 3rd hour. Below the chart sits a legend row with 8px swatches: 'Off-peak', 'Standard', 'Peak'. Tooltip: '18:00 · 2.29 kWh · Peak'.
   Layout: Card at lg col-span-7, full width below. The toggle is 28px tall with a Panel track and 1px Line border, and its active segment is Raised with a Line-strong border and Text color (no lime). Chart height 260px desktop, 220px mobile, measured with ResizeObserver. Rooms: slot width = inner width / 6, bar width min(56px, 40% of slot), rounded-top path with 6px corners. Hours: bar width 60% of slot with 3px corners. Dashed baseline in Line. Bars are focusable (tabIndex 0) and show the tooltip on focus.
   Motion: Bars grow in with a 40ms stagger over 600ms the first time the card is 30% in view. Range change tweens heights over 500ms. A mode switch cross-fades over 150ms and the bars grow in again.
7. **Devices** — id='devices'. Title 'Devices', live subline '5 of 8 on · 2.04 kW'. Header right: a small filter toggle 'All | On | Off' styled like the Rooms/Hours toggle. Rows follow the device table in the data file. Each row has a 36px Raised icon tile (Lime icon when on, Faint when off), the device name (14px 500) over its room (12px Muted), and a 4px usage bar under the text showing kWh today / 6.8 (Lime when on, Line-strong when off) with mono 11 Faint '6.8 kWh today' at its right. On the right side are the live wattage in mono 13 ('1,240 W', or 'Off' in Faint; the EV charger shows 'Scheduled 00:30' in Amber mono 11 while off) and a switch. Switch: a 40×24 button with role='switch', aria-checked and aria-label 'Turn off Heat pump' / 'Turn on Oven'. On, the track is Lime with an Ink knob. Off, the track is Line-strong with a Muted knob. Toggling updates the subline, sidebar badge, gauge, live pill and mini-stats. Turning on a device that pushes the load to 5 kW or more shows a toast: 'Live load is above 5 kW. Peak guard is watching.'
   Layout: Card at lg col-span-5, full width below. Rows are 64px tall with grid columns [36px_1fr_auto_auto], a 12px gap and 1px Line dividers between rows. Hovering a row gives a Raised background at 60%. The filter hides non-matching rows. On mobile the wattage moves under the device name and the usage bar spans the row.
   Motion: Knob slides over 180ms. Wattage counts to its new value over 400ms. The row flashes rgba(198,255,61,0.06) for 600ms. Live wattage updates every 2s from the jitter cycle.
8. **Tariff timeline** — id='tariffs'. Title 'Today's tariff', subline 'Time-of-use rates in [CURRENCY_SYMBOL]/kWh'. Header chip 'Off-peak now' in Lime on Lime glow. Strip segments follow the tariff bands, with labels inside segments of 3h or more: 'Off-peak 0.19', 'Standard 0.28', 'Peak 0.41'. Hour ticks below read '00 06 12 18 24'. A now-marker at 23:52 has a mono 11 Lime label '23:52' above it. Below the strip are two tip rows, each with a Lime Lightbulb icon on a Raised 28px tile and 13px text: 'EV charger is scheduled for 00:30. Charging off-peak saves about [CURRENCY_SYMBOL]3.15 compared with the standard rate.' and 'At 18:12 the oven, hob and heat pump overlapped during peak. Pre-heating the house before 16:00 would trim about 0.9 kW.'
   Layout: Card at lg col-span-7. The strip is 40px tall, radius 10, with overflow hidden and segment widths proportional to hours. Off-peak segments use rgba(198,255,61,0.14) with Lime text, Standard uses Raised with Muted text, and Peak uses rgba(255,181,71,0.16) with Amber text and a 2px Amber top border. Labels are mono 11, centered. The now-marker is a 2px Lime line extending 6px above and below the strip with an 8px Lime dot on top. Tip rows are separated by 12px. On mobile the in-strip labels are hidden and a legend row with swatches and rates appears below the ticks.
   Motion: The now-marker dot loops pulse-ring. Segments fade in left to right with a 50ms stagger when the card first enters view.
9. **Activity** — Title 'Activity', subline 'Today'. Five entries, newest first (time in mono 12 Faint · text): '23:31 · Washing machine started an off-peak cycle', '21:05 · Night schedule lowered the heat pump to 19.5 °C', '18:12 · Peak load reached 3.8 kW while the oven, hob and heat pump overlapped' (Amber dot), '18:20 · Solar production ended — 8.1 kWh generated today' and '07:02 · Morning routine switched on the kitchen circuit'. Keep this order as written.
   Layout: Card at lg col-span-5 (beside the tariff card). Vertical timeline with a 48px time column, then a 1px Line vertical rule with 8px dots (Lime, except Amber for the alert) and 13px Text entries with 16px spacing.
   Motion: Entries fade up with a 50ms stagger when the card first enters view.
10. **Footer strip** — Left: '[BRAND] demo dashboard · data is simulated · Last sync 23:52'. Right: 'v2.4.0'.
   Layout: Inside the main column after the grid: 24px top margin, 1px Line top border, padding 16px 0, 12px Faint text, flex with space-between. It stacks on mobile.
   Motion: None.
11. **Overlays: settings drawer, mobile tab bar, more sheet, toast** — Settings drawer: title 'Display settings', a close X button, and four rows, each with a label, a 12px Muted description and the same Switch. 'Animate charts' (default on; default off when prefers-reduced-motion) 'Draw-ins, morphs and count-ups'. 'Show solar line' (on) 'Overlay generation on the usage chart'. 'Show cost in tooltips' (on) 'Adds grid cost to chart tooltips'. 'Peak guard alerts' (on) 'Warn when live load passes 5 kW'. At the bottom, an outline button 'Reset demo data' restores the default range, devices and settings. Mobile tab bar: five items with icon over an 11px label, Overview, Usage, Rooms, Devices and More (Menu icon). More sheet: the site card, Tariffs link, Settings button, goal card and user row. Toast: Raised, 1px Line-strong border, 3px Amber left border, TriangleAlert icon, 13px text.
   Layout: Settings drawer: fixed right, 380px wide on desktop, full height, Raised background, 1px Line left border, padding 24px. Overlay rgba(0,0,0,0.5). Esc and overlay click close it, focus is trapped inside and returns to the trigger. On mobile it becomes a bottom sheet with max-height 85vh and radius 16 on the top corners. Tab bar (<768px only): fixed bottom, 64px tall plus safe-area inset, rgba(11,15,20,0.92) with blur 12px, 1px Line top border, 5 equal columns. The active item gets a Lime icon and Text label, inactive items are Faint. The More sheet is a bottom sheet with the same styling as the mobile settings sheet. Toast: fixed bottom-right 24px on desktop and above the tab bar on mobile, max-width 340px, role='status'.
   Motion: Drawer and sheet slide in over 280ms, overlay fades over 200ms, toast uses toast-in and fades out after 3000ms. Under reduced motion they appear instantly.

## Assets
- icon: Lucide via lucide-react (ISC): Zap, Wallet, Activity, Sun, LayoutGrid, LineChart, BarChart3, Plug, Receipt, Settings, House, ChevronsUpDown, Check, Calendar, Bell, TriangleAlert, Clock, Lightbulb, MoreHorizontal, Menu, X, ArrowDownRight, ArrowUpRight, Thermometer, Refrigerator, WashingMachine, Monitor, Lamp, Tv, CookingPot, PlugZap. Size 16–18px, strokeWidth 1.75. If an icon name is missing from the installed version, use the closest Lucide icon.
- font: Self-hosted through Fontsource npm packages imported in src/main.tsx: @fontsource-variable/inter, @fontsource-variable/space-grotesk, and @fontsource/jetbrains-mono (400 and 500). All are OFL-1.1.
- image: No bitmap images. The logo mark, favicon (public/favicon.svg), initials avatar, charts, sparklines, gauge and tariff strip are all inline SVG or CSS drawn in code. ASSETS.md lists the fonts and the icon package with source URLs and licenses.

## Don'ts
- No backend: no server code, API routes, databases, auth, third-party APIs or forms that submit anywhere. All data is local and fake, in src/data/energy.ts.
- No chart or visualisation libraries (Recharts, Chart.js, D3, Nivo, ECharts, ApexCharts, Victory) and no animation libraries. Every chart and tween is hand-written SVG and requestAnimationFrame code.
- Do not add dependencies beyond the listed stack, and do not change the stack unless the user asks.
- No Tailwind Plus / Tailwind UI, Aceternity UI, ThemeForest or purchased template source.
- No real energy-company, utility, solar, smart-home or app names, logos or device brands. Use [BRAND] and the generic device names given.
- No copy, slogans or metric wording copied from existing energy apps. Use only the copy written here, rewritten to fit the variables.
- No brand palettes from existing dashboards. Use only the listed hex values, keep Lime as the single accent and Amber only for peaks and warnings. No light theme.
- No photos of people and no avatar images. Avatars are code-drawn initials only.
- No commercial or custom fonts, and no font loading from fonts.googleapis.com, fonts.gstatic.com, Adobe Fonts or any CDN. Fonts come only from the Fontsource packages.
- No CDN <script> or <link> tags, no hotlinked files, no analytics or tracking.
- No Math.random and no system-clock dates. Every number and date is fixed in the data file so every load looks identical.
- Production build: minified, no source maps, nothing from src/ copied into dist/, relative asset paths (base './'), dist/ under 20 MB.
- Internal navigation uses in-page hash anchors (#usage, #rooms, #devices, #tariffs) or file names like ./index.html, never bare directory links. No dead links.
- Zero console errors and no horizontal page scroll at 1440×900 and 390×844.
- Respect prefers-reduced-motion: disable draw-ins, morphs, staggers, pulses, slides and smooth scrolling.
- No SEO extras: no meta descriptions, Open Graph tags, JSON-LD or sitemaps. Just the <title> and favicon.
- No mention of VibeFather anywhere in the site except an optional credit link if the prompt adds one.

---
— Prompt by VibeFather · https://vibefather.ai/t/wattline · CC BY-NC 4.0 (non-commercial; commercial use needs a VibeFather Pro license) · Request removal: https://vibefather.ai/takedown

Free for personal projects under CC BY-NC 4.0. Commercial use comes with Pro.

DESIGN.md

The design system on its own — colors, type, spacing and motion. Apply this look to a project you already have.

---
name: "Wattline"
colors:
  ink: "#0B0F14"
  panel: "#11161D"
  raised: "#161D26"
  line: "#1F2833"
  line_strong: "#2A3542"
  text: "#E6EDF3"
  muted: "#8B98A8"
  faint: "#5A6675"
  lime: "#C6FF3D"
  lime_deep: "#8FBF1F"
  amber: "#FFB547"
  lime_glow: "rgba(198,255,61,0.10)"
  amber_glow: "rgba(255,181,71,0.12)"
typography:
  display_greeting_brand_wordmark_kpi_and_gauge_numbers_card_titles_on_mobile: { family: "Space Grotesk Variable (@fontsource-variable/space-grotesk)", weights: [500, 600] }
  ui_text_nav_labels_body_buttons: { family: "Inter Variable (@fontsource-variable/inter)", weights: [400, 500, 600] }
  data_axis_labels_timestamps_wattage_readouts_tooltip_values_chips: { family: "JetBrains Mono (@fontsource/jetbrains-mono)", weights: [400, 500] }
spacing: "4px base unit. Desktop main area: padding 28px top, 32px sides, 40px bottom, max-width 1320px. 12-column grid with a 20px gap. Card padding 20px, card header to content gap 16px. Mobile (<768px): padding 16px, grid gap 12px, card padding 16px, and 88px bottom padding to clear the tab bar. All numbers use tabular figures (.num)."
radius: "Cards 16px. Buttons, inputs, icon tiles and nav items 10px. Segmented controls 12px outer, 9px inner pill. Chips and switches fully rounded. Bars 6px top corners (rooms) and 3px (hours). Tooltips 10px."
---

# Wattline — Design System

Apply this design system to an existing project. It describes the look and feel only; for the full site, use prompt.md.

## Colors
- **Ink** `#0B0F14` — App background, sidebar background, text on lime
- **Panel** `#11161D` — Card surfaces, segmented-control track
- **Raised** `#161D26` — Active nav item, icon tiles, tooltips, hover rows, drawers
- **Line** `#1F2833` — 1px borders, dividers, chart gridlines, gauge track
- **Line strong** `#2A3542` — Hover borders, inactive bars, switch-off track, crosshair
- **Text** `#E6EDF3` — Primary text and numbers
- **Muted** `#8B98A8` — Secondary text, nav labels, solar line
- **Faint** `#5A6675` — Axis labels, captions, section labels, off-state icons
- **Lime** `#C6FF3D` — The single accent: active range pill, usage line, on-state switches, good deltas, focus ring, live dot
- **Lime deep** `#8FBF1F` — Start of the gauge arc gradient (to Lime)
- **Amber** `#FFB547` — Peaks, peak-tariff band, bad deltas, peak-guard warning, notification dot
- **Lime glow** `rgba(198,255,61,0.10)` — Radial glow behind charts, good-delta chip background, off-peak band at 0.14
- **Amber glow** `rgba(255,181,71,0.12)` — Bad-delta chip background, peak band at 0.16

## Typography
- **Display: greeting, brand wordmark, KPI and gauge numbers, card titles on mobile** — Space Grotesk Variable (@fontsource-variable/space-grotesk) (OFL), weights 500/600. Self-host the font files.
- **UI text: nav, labels, body, buttons** — Inter Variable (@fontsource-variable/inter) (OFL), weights 400/500/600. Self-host the font files.
- **Data: axis labels, timestamps, wattage readouts, tooltip values, chips** — JetBrains Mono (@fontsource/jetbrains-mono) (OFL), weights 400/500. Self-host the font files.

## Spacing & shape
- Spacing: 4px base unit. Desktop main area: padding 28px top, 32px sides, 40px bottom, max-width 1320px. 12-column grid with a 20px gap. Card padding 20px, card header to content gap 16px. Mobile (<768px): padding 16px, grid gap 12px, card padding 16px, and 88px bottom padding to clear the tab bar. All numbers use tabular figures (.num).
- Radius: Cards 16px. Buttons, inputs, icon tiles and nav items 10px. Segmented controls 12px outer, 9px inner pill. Chips and switches fully rounded. Bars 6px top corners (rooms) and 3px (hours). Tooltips 10px.

## Motion
- UI easing cubic-bezier(0.22,1,0.36,1). Data tweens use easeInOutCubic (morphs) or easeOutCubic (count-ups and grow-ins), driven by requestAnimationFrame.
- On load, cards fade up 12px over 450ms, staggered 60ms in DOM order (KPI tiles first).
- KPI values count up from the previous value to the new one over 900ms easeOutCubic. Delta chips cross-fade over 200ms.
- Usage chart: on first view the usage line draws with stroke-dashoffset from full length to 0 over 1200ms, and the area fills from opacity 0 to 1 over 600ms after a 300ms delay. On range change, both series and the y-scale max morph over 700ms, and x labels fade out over 150ms and back in over 200ms.
- Bars grow from scaleY(0) with transform-origin at the bottom over 600ms easeOutCubic, staggered 40ms, the first time the card is 30% in view. On range change their heights tween over 500ms. On mode switch the chart cross-fades over 150ms and the bars grow in again.
- The range switcher pill slides with translateX over 320ms. The switch knob slides 16px over 180ms. The gauge arc and center number tween over 600ms. Device wattage counts to its new value over 400ms, and the toggled row flashes rgba(198,255,61,0.06) for 600ms.
- The live dot (top-bar pill and tariff now-marker) loops the pulse-ring animation every 2s.
- Drawers slide in over 280ms with a 200ms overlay fade. Toasts use toast-in and auto-dismiss after 3000ms.
- prefers-reduced-motion: no draw-ins, morphs, staggers, pulses or slides. Values and positions change instantly, and the live numbers still update every 2s without tweening.

## Imagery
No photos or bitmaps. Everything visual is SVG or CSS drawn in code: charts, sparklines, gauge, tariff strip, the logo mark, and initials avatars on Raised circles. Lucide icons are used at 16–18px with a 1.75 stroke. Depth comes only from 1px borders, soft radial lime glows behind the chart cards (radial-gradient(600px circle at 30% 0%, rgba(198,255,61,0.08), transparent 60%)) and drop-shadow glows on active data points. Tooltips and drawers use the shadow 0 8px 24px rgba(0,0,0,0.4). No light theme.

## Tone
Calm, precise, quietly technical. Short labels, sentence case, numbers first. Friendly but never cute. No exclamation marks.

---
— Prompt by VibeFather · https://vibefather.ai/t/wattline · CC BY-NC 4.0 (non-commercial; commercial use needs a VibeFather Pro license) · Request removal: https://vibefather.ai/takedown