An open API service indexing awesome lists of open source software.

https://github.com/pwelty/slate

A Python-powered personal dashboard with YAML configuration, server-side rendering, and beautiful themes. Self-hosted, secure, and fast - bringing together all your services in one interface.
https://github.com/pwelty/slate

dashboard homelab personal-dashboard python self-hosted server-side-rendering tailscale themes widgets yaml

Last synced: 2 months ago
JSON representation

A Python-powered personal dashboard with YAML configuration, server-side rendering, and beautiful themes. Self-hosted, secure, and fast - bringing together all your services in one interface.

Awesome Lists containing this project

README

          

# Slate

[![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![GitHub release](https://img.shields.io/github/v/release/pwelty/slate.svg)](https://github.com/pwelty/slate/releases)

**A self-hosted dashboard that outputs pure HTML and CSS. No JavaScript. No framework. No runtime.**

Slate fetches data from your services at build time and renders a static HTML file. The result works on anything with an HTML renderer — standard browsers, e-ink displays, screen readers, kiosk screens, `curl`.

![Slate Dashboard](src/screenshots/slate%20db%20v1.png)

## How it works

1. You define your dashboard layout and widgets in YAML
2. Slate's Python build step fetches data from your services (weather, tasks, bookmarks, etc.)
3. Everything is rendered into a single static HTML+CSS file
4. Serve it from anywhere — any web server, a CDN, a NAS, a Raspberry Pi

**API keys stay on the server.** They're used at build time, never shipped to the browser. Rebuild on a cron to keep data fresh.

## Why Slate instead of Dashy, Homepage, Homarr, etc.?

Every other self-hosted dashboard is a client-side application. Your browser loads a JavaScript bundle, makes API calls to your services, and renders the results. That means:

- Your API keys are in the browser
- Every page load triggers a cascade of network requests
- You need CORS proxies or middleware for most integrations
- Nothing renders without a JavaScript runtime

Slate takes a fundamentally different approach. All data fetching happens server-side at build time. The output is flat HTML and CSS — no JavaScript required. This makes it:

- **Secure** — API keys never leave the server
- **Fast** — no client-side fetching, no loading spinners, instant render
- **Universal** — works on any device that can render HTML
- **Simple** — no runtime, no proxy, no CORS configuration
- **Resilient** — the last successful build always works, even if a service is down

## Quick start

```bash
git clone https://github.com/pwelty/slate.git
cd slate
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt

cp config/dashboard-example.yaml config/dashboard.yaml
# Edit config/dashboard.yaml with your services and API keys

python3 src/scripts/dashboard_renderer.py
python3 src/scripts/serve.py
# Open http://localhost:5173
```

## Configuration

Your dashboard is defined in `config/dashboard.yaml`:

```yaml
dashboard:
title: "My dashboard"
theme: "paper"

components:
- id: "weather"
widget: "weather"
position: { row: 1, column: 1, width: 4, height: 2 }
config:
location: "30033"
apiKey: "YOUR_OPENWEATHER_KEY"
units: "fahrenheit"

- id: "tasks"
widget: "todoist"
position: { row: 1, column: 5, width: 4, height: 2 }
config:
apiToken: "YOUR_TODOIST_TOKEN"
limit: 8
```

## Available widgets

| Widget | Description |
|--------|-------------|
| **weather** | Current conditions via OpenWeatherMap |
| **forecast** | Multi-day weather forecast |
| **radar** | Weather radar map |
| **todoist** | Tasks with priority indicators |
| **trilium** | Recent notes from Trilium |
| **linkwarden** | Bookmarks from Linkwarden |
| **obsidian** | Notes via Obsidian Local REST API |
| **pihole** | Pi-hole ad blocking stats (v5 and v6+) |
| **rss** | RSS feed aggregation |
| **clock** | Date and time (rendered at build) |
| **link** | Service shortcuts |
| **text** | Custom content |
| **image** | Image display |
| **status-summary** | System health overview |

## Themes

Themes are YAML files that define colors, typography, and spacing. Pick one in your config:

| Theme | Description |
|-------|-------------|
| **paper** | Clean, classic, document-like |
| **dark** | Professional dark |
| **light** | Minimal light |
| **ocean** | Deep blue |
| **tokyo-night** | Developer favorite |
| **synthwave** | 80s neon |
| **retro** | Terminal aesthetic |
| **minimal-dark** | Stripped-down dark |

Create your own by copying any theme YAML and editing the values.

## Development

```bash
# Auto-rebuild on file changes
python3 scripts/auto-rebuild.py

# Manual build
python3 src/scripts/dashboard_renderer.py

# Serve locally
python3 src/scripts/serve.py
```

## Deployment

The build output is a static `dist/` directory. Serve it however you want:

```bash
# Simple Python server
cd dist && python3 -m http.server 8080

# Nginx, Apache, Caddy — just point at dist/

# Docker
docker-compose up

# Tailscale for secure remote access
tailscale serve / http://localhost:5173
# Access at https://slate.yourname.ts.net
```

Rebuild on a schedule (cron, systemd timer, etc.) to keep widget data fresh.

## Creating widgets

Widgets are YAML files in `src/widgets/`. Each defines a schema, optional data processing (Python), HTML template, and CSS:

```yaml
extends: "widget"

metadata:
type: "api"
description: "My custom widget"

schema:
apiKey:
type: "string"
required: true

dataProcessing:
generateData: |
import requests
response = requests.get("https://api.example.com",
headers={"Authorization": config['apiKey']})
result = response.json()

widget-body: |

{{ result.data }}

css: |
.my-widget { color: var(--color-text); }
```

See [widget definitions](docs/WIDGET_DEFINITIONS.md) for the full specification.

## Documentation

- [Configuration guide](docs/CONFIGURATION.md)
- [Widget reference](docs/WIDGET_DEFINITIONS.md)
- [Widget YAML specification](docs/WIDGET_YAML_SPECIFICATION.md)
- [Theming architecture](docs/THEMING_ARCHITECTURE.md)
- [Contributing](docs/CONTRIBUTING.md)

## Alternatives

Slate is one of many self-hosted dashboard projects. These are excellent and may be a better fit depending on your needs:

- [Dashy](https://dashy.to/) — highly customizable, 50+ widgets, extensive theming
- [Homepage](https://gethomepage.dev/) — modern design, 100+ service integrations
- [Homarr](https://homarr.dev/) — GUI configuration, user management
- [Heimdall](https://github.com/linuxserver/Heimdall) — simple application launcher
- [Homer](https://github.com/bastienwirtz/homer) — minimal static homepage

Slate's approach is different: server-side data fetching with pure HTML+CSS output, no JavaScript runtime required.

## Contributing

Contributions are welcome — bug reports, widgets, themes, documentation.

- [Issues](https://github.com/pwelty/slate/issues)
- [Discussions](https://github.com/pwelty/slate/discussions)

## License

MIT License © 2025 Paul Welty