https://github.com/shiftbloom-studio/circadian-ui
A theme system that automatically adjusts colors, contrasts, and brightness to the time of day and usage situation. Your app feels awake in the morning and calm in the evening—without users having to manually switch between modes.
https://github.com/shiftbloom-studio/circadian-ui
npm-package react tailwindcss theme-ui
Last synced: 6 months ago
JSON representation
A theme system that automatically adjusts colors, contrasts, and brightness to the time of day and usage situation. Your app feels awake in the morning and calm in the evening—without users having to manually switch between modes.
- Host: GitHub
- URL: https://github.com/shiftbloom-studio/circadian-ui
- Owner: shiftbloom-studio
- License: mit
- Created: 2026-01-04T14:29:12.000Z (6 months ago)
- Default Branch: main
- Last Pushed: 2026-01-11T15:01:51.000Z (6 months ago)
- Last Synced: 2026-01-13T19:45:55.115Z (6 months ago)
- Topics: npm-package, react, tailwindcss, theme-ui
- Language: TypeScript
- Homepage:
- Size: 733 KB
- Stars: 1
- Watchers: 1
- Forks: 0
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Security: SECURITY.md
Awesome Lists containing this project
README
# Circadian UI
[](https://www.npmjs.com/package/@shiftbloom-studio/circadian-ui)
[](https://github.com/shiftbloom-studio/circadian-ui/actions/workflows/ci.yml)
[](LICENSE)
**Circadian UI** is a production‑ready, time‑aware theming engine for React and Tailwind. It adapts your design tokens across dawn/day/dusk/night based on local time, optional sunrise/sunset data, system preferences, and user overrides — while enforcing accessible contrast.
## What makes it special
- **Zero‑config start** — install, wrap your app, and you’re done.
- **Circadian magic** — automatic phase shifts based on time or sun data.
- **Accessible by default** — WCAG‑conscious contrast adjustments.
- **Framework‑friendly** — Next.js (App/Pages), Vite, SSR or CSR.
- **Tailwind‑native** — tokens exposed as CSS variables + preset/plugin.
---
## Install
```bash
npm install @shiftbloom-studio/circadian-ui
```
---
## 30‑second quickstart (React)
```tsx
import { CircadianProvider, CircadianScript } from "@shiftbloom-studio/circadian-ui";
export function App({ children }: { children: React.ReactNode }) {
return (
<>
{children}
>
);
}
```
> `CircadianScript` prevents theme flash by setting the initial phase before hydration.
---
## Next.js
### App Router
```tsx
// app/layout.tsx
import "./globals.css";
import { CircadianProvider, CircadianScript } from "@shiftbloom-studio/circadian-ui";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
{children}
);
}
```
### Pages Router
```tsx
// pages/_document.tsx
import Document, { Head, Html, Main, NextScript } from "next/document";
import { CircadianScript } from "@shiftbloom-studio/circadian-ui";
export default class MyDocument extends Document {
render() {
return (
);
}
}
```
---
## Vite / CSR apps
```tsx
import { createRoot } from "react-dom/client";
import { CircadianProvider, CircadianScript } from "@shiftbloom-studio/circadian-ui";
import App from "./App";
createRoot(document.getElementById("root")!).render(
<>
>
);
```
---
## Tailwind integration
### Option A — CSS variables (recommended)
```ts
// tailwind.config.ts
import type { Config } from "tailwindcss";
const config: Config = {
content: ["./app/**/*.{ts,tsx}", "./components/**/*.{ts,tsx}"],
theme: {
extend: {
colors: {
background: "hsl(var(--cui-bg) / )",
foreground: "hsl(var(--cui-fg) / )",
muted: "hsl(var(--cui-muted) / )",
"muted-foreground": "hsl(var(--cui-muted-fg) / )",
card: "hsl(var(--cui-card) / )",
"card-foreground": "hsl(var(--cui-card-fg) / )",
border: "hsl(var(--cui-border) / )",
ring: "hsl(var(--cui-ring) / )",
accent: "hsl(var(--cui-accent) / )",
"accent-foreground": "hsl(var(--cui-accent-fg) / )",
destructive: "hsl(var(--cui-destructive) / )",
"destructive-foreground": "hsl(var(--cui-destructive-fg) / )"
}
}
}
};
export default config;
```
### Option B — Preset + Plugin
```ts
// tailwind.config.ts
import type { Config } from "tailwindcss";
import plugin from "tailwindcss/plugin";
import { circadianPlugin, circadianTailwindPreset } from "@shiftbloom-studio/circadian-ui";
const config: Config = {
presets: [circadianTailwindPreset()],
plugins: [plugin(circadianPlugin())]
};
export default config;
```
---
## Configuration examples
### 1) Custom schedule windows
```tsx
{children}
```
### 2) Sun‑aware schedule
```ts
import type { SunTimesProvider } from "@shiftbloom-studio/circadian-ui";
const provider: SunTimesProvider = (date) => {
return {
sunrise: new Date(date.getFullYear(), date.getMonth(), date.getDate(), 6, 12),
sunset: new Date(date.getFullYear(), date.getMonth(), date.getDate(), 19, 48)
};
};
;
```
### 3) Auto mode (recommended)
```tsx
```
If sun data is available, it uses `sun`; otherwise it falls back to `time`.
### 4) Manual override UI
```tsx
import { useCircadian } from "@shiftbloom-studio/circadian-ui";
const ModeToggle = () => {
const { mode, resolvedMode, setMode, setPhaseOverride } = useCircadian();
return (
setMode("auto")}>Auto
setPhaseOverride("night")}>Night
Requested: {mode}
Resolved: {resolvedMode}
);
};
```
### 5) Initial phase hint (SSR)
```tsx
```
### 6) Disable persistence
```tsx
```
### 7) Strict contrast tuning
```tsx
```
---
## Design tokens
| Token | CSS Variable |
| ---------------------- | ---------------------- |
| Background | `--cui-bg` |
| Foreground | `--cui-fg` |
| Muted | `--cui-muted` |
| Muted Foreground | `--cui-muted-fg` |
| Card | `--cui-card` |
| Card Foreground | `--cui-card-fg` |
| Border | `--cui-border` |
| Ring | `--cui-ring` |
| Accent | `--cui-accent` |
| Accent Foreground | `--cui-accent-fg` |
| Destructive | `--cui-destructive` |
| Destructive Foreground | `--cui-destructive-fg` |
---
## API reference
### React
- `CircadianProvider`
- Props: `{ config?: CircadianConfig; children: React.ReactNode }`
- Applies phase + tokens to the document root (or body).
- `useCircadian()`
- Returns `{ phase, mode, resolvedMode, setMode, setPhaseOverride, clearOverride, tokens, isAuto, nextChangeAt }`.
- `useCircadianTokens()`
- Returns `{ tokens, cssVars, applyToStyle }` for inline usage.
- `CircadianScript`
- Inline script component to prevent flash before hydration.
### Core utilities
- `getPhaseFromTime(date, schedule)`
- `getPhaseFromSunTimes(date, sunTimes, options)`
- `computeNextTransition(date, schedule)`
- `ensureContrast(tokens, options)`
- `resolveMode(userMode, systemPrefs, config)`
- `createInlineScript(config)`
### Tailwind
- `circadianTailwindPreset()`
- `circadianPlugin()` (wrap with `tailwindcss/plugin`)
---
## Configuration schema
```ts
interface CircadianConfig {
schedule?: Partial;
tokens?: Partial>>;
mode?: "time" | "sun" | "manual" | "auto";
sunTimesProvider?: SunTimesProvider;
sunSchedule?: Partial;
initialPhase?: Phase;
persist?: boolean;
storageKey?: string;
accessibility?: Partial;
system?: Partial;
colorSchemeBias?: Partial;
transition?: Partial;
setAttributeOn?: "html" | "body";
}
```
---
## Accessibility notes
Circadian UI nudges foreground tokens until they meet your configured contrast ratio. You can tune ratios for normal and large text via `accessibility.minimumRatio` and `accessibility.largeTextRatio`.
---
## Development
```bash
npm ci
npm run lint
npm run typecheck
npm run test
npm run build
```