Openstatus www.openstatus.dev

chore: theme names, unique ids and improved README (#1485)

* fix: theme names

* fix: theme names

* chore: runtime validation for unique theme id

* chore: docs

* chore: README

* chore: README

* chore: README

authored by

Maximilian Kaske and committed by
GitHub
a9ab060b e67f4452

+96 -40
+6 -2
README.md
··· 129 129 pnpm dx 130 130 ``` 131 131 132 - 4. Launch the web app 132 + 4. Launch whatever app you wish to: 133 133 134 134 ```sh 135 135 pnpm dev:web 136 + pnpm dev:status-page 137 + pnpm dev:dashboard 136 138 ``` 137 139 140 + The above commands whill automatically run the libSQL client on `8080` so you might wanna kill the turso command from step 3. 141 + 138 142 5. See the results: 139 143 140 - - open [http://localhost:3000](http://localhost:3000) for the web app 144 + - open [http://localhost:3000](http://localhost:3000) (default port) 141 145 142 146 ### Videos 143 147
+36 -6
packages/theme-store/README.md
··· 1 1 # Community Themes 2 2 3 + **Help us build epic status page themes!** 4 + 3 5 This directory contains community-contributed themes for openstatus status pages. These themes allow users to customize the visual appearance of their status pages with different color schemes and design styles. 4 6 5 7 ## What are Community Themes? ··· 12 14 13 15 ## Themes Examples 14 16 15 - - **Default** - The standard openstatus theme 17 + - **Openstatus** - The standard openstatus theme 18 + - **Openstatus (Rounded)** - The rounded openstatus theme (similar to the legacy page) 19 + - **Supabase** - Theme matching Supabase's brand colors 16 20 - **GitHub (High Contrast)** - High contrast theme inspired by GitHub's design 17 - - **Supabase** - Theme matching Supabase's brand colors 18 21 19 22 ## Creating a New Theme 20 23 ··· 23 26 24 27 Want to contribute a theme? Follow these steps: 25 28 26 - ### 1. Fork the Repository 27 - Start by forking the openstatus repository to your GitHub account. 29 + ### 1. Run the project 30 + 31 + Start by forking the openstatus repository to your GitHub account and run the command `dev:status-page` locally following [Getting Started](https://github.com/openstatusHQ/openstatus?tab=readme-ov-file#getting-started-) steps. 32 + 33 + Once you run the `@openstatus/status-page` app, e.g. via the following command in the root directory: 34 + 35 + ```bash 36 + pnpm dev:status-page 37 + ``` 38 + 39 + You can either access [localhost:3000](http://localhost:3000) to see the theme explorer or [localhost:3000/status](http://localhost:3000/status) to have a status page with seeded entries. On the bottom right is a configration button which has access to settings, including the community theme. 40 + 41 + > If something is off, our you think improvements can be made, feel free to raise an issue, join Discord, or DM us! 28 42 29 43 ### 2. Create Your Theme File 44 + 30 45 Create a new TypeScript file in this directory (e.g., `my-theme.ts`). You can copy an existing theme file as a starting template. 31 46 32 47 ### 3. Define Your Theme 48 + 33 49 Your theme file should export a constant that matches the `Theme` interface: 34 50 35 51 ```typescript ··· 40 56 name: "My Awesome Theme", // Display name 41 57 author: { 42 58 name: "@yourusername", 43 - url: "https://github.com/yourusername" 59 + url: "https://github.com/yourusername" // Add your personal website or a social link 44 60 }, 45 61 light: { 46 62 // CSS custom properties for light mode ··· 57 73 } as const satisfies Theme; 58 74 ``` 59 75 76 + You don't need to add every single css var from the `THEME_VAR_NAMES` list. 77 + 60 78 ### 4. Add to Theme Registry 79 + 61 80 Update `index.ts` to include your theme in the `THEMES_LIST` array. 62 81 82 + ```typescript 83 + const THEMES_LIST = [ 84 + OPENSTATUS_THEME, 85 + OPENSTATUS_ROUNDED_THEME, 86 + //... 87 + MY_THEME 88 + ] satisfies Theme[]; 89 + 90 + ``` 91 + 63 92 ### 5. Submit a Pull Request 93 + 64 94 Create a pull request with your theme for review. 65 95 66 96 ## Design Guidelines ··· 94 124 3. Check accessibility with browser dev tools 95 125 4. Ensure all status indicators (operational, degraded, etc.) are clearly distinguishable 96 126 97 - To test a theme, you can use the `sessionStorage.setItem("community-theme", "true");` on your stpg.dev or vercel preview link. It will open a floating button on the right left corner where you can choose between the themes and dark/light mode. 127 + To test a theme on non-development environment, you can use the `sessionStorage.setItem("community-theme", "true");`. It will open a floating button on the right left corner where you can choose between the themes and dark/light mode. 98 128 99 129 ## Questions? 100 130
+7 -7
packages/theme-store/src/default.ts packages/theme-store/src/openstatus.ts
··· 1 1 import type { Theme } from "./types"; 2 2 3 - export const DEFAULT_THEME = { 3 + export const OPENSTATUS_THEME = { 4 4 id: "default" as const, 5 - name: "Default", 5 + name: "Openstatus", 6 6 author: { name: "@openstatus", url: "https://openstatus.dev" }, 7 7 light: { 8 8 "--background": "oklch(100% 0 0)", ··· 46 46 }, 47 47 } as const satisfies Theme; 48 48 49 - export const DEFAULT_ROUNDED_THEME = { 49 + export const OPENSTATUS_ROUNDED_THEME = { 50 50 id: "default-rounded" as const, 51 - name: "Default (Rounded)", 52 - author: DEFAULT_THEME.author, 51 + name: "Openstatus (Rounded)", 52 + author: OPENSTATUS_THEME.author, 53 53 light: { 54 - ...DEFAULT_THEME.light, 54 + ...OPENSTATUS_THEME.light, 55 55 "--radius": "0.625rem", 56 56 }, 57 57 dark: { 58 - ...DEFAULT_THEME.dark, 58 + ...OPENSTATUS_THEME.dark, 59 59 "--radius": "0.625rem", 60 60 }, 61 61 } as const satisfies Theme;
+2 -2
packages/theme-store/src/github-contrast.ts packages/theme-store/src/github.ts
··· 1 1 import type { Theme } from "./types"; 2 2 3 - export const GITHUB_CONTRAST = { 3 + export const GITHUB_HIGH_CONTRAST_THEME = { 4 4 id: "github-contrast", 5 - name: "Github (High Contrast)", 5 + name: "GitHub (High Contrast)", 6 6 author: { name: "@openstatus", url: "https://openstatus.dev" }, 7 7 light: { 8 8 "--background": "oklch(100% 0 0)",
+19 -10
packages/theme-store/src/index.ts
··· 1 1 export * from "./types"; 2 - import { DEFAULT_ROUNDED_THEME, DEFAULT_THEME } from "./default"; 3 - import { GITHUB_CONTRAST } from "./github-contrast"; 4 - import { SUPABASE } from "./supabase"; 2 + import { GITHUB_HIGH_CONTRAST_THEME } from "./github"; 3 + import { OPENSTATUS_ROUNDED_THEME, OPENSTATUS_THEME } from "./openstatus"; 4 + import { SUPABASE_THEME } from "./supabase"; 5 5 import type { Theme, ThemeMap } from "./types"; 6 + import { assertUniqueThemeIds } from "./utils"; 6 7 7 - // TODO: Add validation to ensure that the theme IDs are unique 8 8 const THEMES_LIST = [ 9 - DEFAULT_THEME, 10 - DEFAULT_ROUNDED_THEME, 11 - SUPABASE, 12 - GITHUB_CONTRAST, 9 + OPENSTATUS_THEME, 10 + OPENSTATUS_ROUNDED_THEME, 11 + SUPABASE_THEME, 12 + GITHUB_HIGH_CONTRAST_THEME, 13 13 ] satisfies Theme[]; 14 + 15 + // NOTE: runtime validation to ensure that the theme IDs are unique 16 + assertUniqueThemeIds(THEMES_LIST); 14 17 15 18 export const THEMES = THEMES_LIST.reduce<ThemeMap>((acc, theme) => { 16 19 acc[theme.id as keyof ThemeMap] = theme; ··· 20 23 export const THEME_KEYS = THEMES_LIST.map((theme) => theme.id); 21 24 export type ThemeKey = (typeof THEME_KEYS)[number]; 22 25 23 - export function generateThemeStyles(themeKey: ThemeKey = "default") { 24 - const theme = THEMES[themeKey]; 26 + export function generateThemeStyles(themeKey?: string) { 27 + let theme = themeKey ? THEMES[themeKey] : undefined; 28 + 29 + if (!theme) { 30 + // NOTE: fallback to openstatus theme if no theme is found 31 + theme = OPENSTATUS_THEME; 32 + } 33 + 25 34 const lightVars = Object.entries(theme.light) 26 35 .map(([key, value]) => `${key}: ${value};`) 27 36 .join("\n ");
+1 -1
packages/theme-store/src/supabase.ts
··· 1 1 import type { Theme } from "./types"; 2 2 3 - export const SUPABASE = { 3 + export const SUPABASE_THEME = { 4 4 id: "supabase", 5 5 name: "Supabase", 6 6 author: { name: "@supabase", url: "https://supabase.com/" },
+12 -12
packages/theme-store/src/types.ts
··· 1 1 export const THEME_VAR_NAMES = [ 2 + // NOTE: default shadcn/ui colors 2 3 "--radius", 3 4 "--background", 4 5 "--foreground", ··· 14 15 "--muted-foreground", 15 16 "--accent", 16 17 "--accent-foreground", 17 - "--destructive", 18 18 "--border", 19 19 "--input", 20 20 "--ring", 21 + "--destructive", // red, outage/error status 22 + // NOTE: the following colors are used for the public monitors UI to differentiate the percentiles of the response times 21 23 "--chart-1", 22 24 "--chart-2", 23 25 "--chart-3", 24 26 "--chart-4", 25 27 "--chart-5", 26 - "--sidebar", 27 - "--sidebar-foreground", 28 - "--sidebar-primary", 29 - "--sidebar-primary-foreground", 30 - "--sidebar-accent", 31 - "--sidebar-accent-foreground", 32 - "--sidebar-border", 33 - "--sidebar-ring", 34 - "--success", 35 - "--warning", 36 - "--info", 28 + 29 + // NOTE: the following colors are not part of shadcn/ui, but are essential part of the status page 30 + "--success", // green, operational status 31 + "--warning", // yellow, degraded status 32 + "--info", // blue, monitoring status 33 + 34 + // NOTE: the following colors are used for the public monitors UI to differentiate the different regions 35 + // It is not required to add them to your custom theme, but you can if you want to. 36 + // DEFAULT: https://github.com/openstatusHQ/openstatus/blob/main/apps/status-page/src/app/globals.css#L98 37 37 "--rainbow-1", 38 38 "--rainbow-2", 39 39 "--rainbow-3",
+13
packages/theme-store/src/utils.ts
··· 1 + import type { Theme } from "./types"; 2 + 3 + export function assertUniqueThemeIds(themes: Theme[]) { 4 + const seen = new Set<string>(); 5 + for (const theme of themes) { 6 + if (seen.has(theme.id)) { 7 + throw new Error( 8 + `Duplicate theme ID detected: "${theme.id}" in theme "${theme.name}". All theme IDs must be unique.`, 9 + ); 10 + } 11 + seen.add(theme.id); 12 + } 13 + }