{"id":35570964,"url":"https://github.com/shiftbloom-studio/circadian-ui","last_synced_at":"2026-01-18T04:36:58.457Z","repository":{"id":332409207,"uuid":"1127752947","full_name":"shiftbloom-studio/circadian-ui","owner":"shiftbloom-studio","description":"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.","archived":false,"fork":false,"pushed_at":"2026-01-11T15:01:51.000Z","size":751,"stargazers_count":1,"open_issues_count":2,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-01-13T19:45:55.115Z","etag":null,"topics":["npm-package","react","tailwindcss","theme-ui"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/shiftbloom-studio.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-01-04T14:29:12.000Z","updated_at":"2026-01-07T01:27:58.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/shiftbloom-studio/circadian-ui","commit_stats":null,"previous_names":["shiftbloom-studio/circadian-ui"],"tags_count":1,"template":false,"template_full_name":"shiftbloom-studio/npm-package-template","purl":"pkg:github/shiftbloom-studio/circadian-ui","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shiftbloom-studio%2Fcircadian-ui","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shiftbloom-studio%2Fcircadian-ui/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shiftbloom-studio%2Fcircadian-ui/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shiftbloom-studio%2Fcircadian-ui/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/shiftbloom-studio","download_url":"https://codeload.github.com/shiftbloom-studio/circadian-ui/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shiftbloom-studio%2Fcircadian-ui/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28529875,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-18T00:39:45.795Z","status":"online","status_checked_at":"2026-01-18T02:00:07.578Z","response_time":98,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["npm-package","react","tailwindcss","theme-ui"],"created_at":"2026-01-04T17:16:18.196Z","updated_at":"2026-01-18T04:36:58.452Z","avatar_url":"https://github.com/shiftbloom-studio.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Circadian UI\n\n[![npm version](https://img.shields.io/npm/v/@shiftbloom-studio/circadian-ui)](https://www.npmjs.com/package/@shiftbloom-studio/circadian-ui)\n[![CI](https://github.com/shiftbloom-studio/circadian-ui/actions/workflows/ci.yml/badge.svg)](https://github.com/shiftbloom-studio/circadian-ui/actions/workflows/ci.yml)\n[![license](https://img.shields.io/npm/l/@shiftbloom-studio/circadian-ui)](LICENSE)\n\n**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.\n\n## What makes it special\n\n- **Zero‑config start** — install, wrap your app, and you’re done.\n- **Circadian magic** — automatic phase shifts based on time or sun data.\n- **Accessible by default** — WCAG‑conscious contrast adjustments.\n- **Framework‑friendly** — Next.js (App/Pages), Vite, SSR or CSR.\n- **Tailwind‑native** — tokens exposed as CSS variables + preset/plugin.\n\n---\n\n## Install\n\n```bash\nnpm install @shiftbloom-studio/circadian-ui\n```\n\n---\n\n## 30‑second quickstart (React)\n\n```tsx\nimport { CircadianProvider, CircadianScript } from \"@shiftbloom-studio/circadian-ui\";\n\nexport function App({ children }: { children: React.ReactNode }) {\n  return (\n    \u003c\u003e\n      \u003cCircadianScript /\u003e\n      \u003cCircadianProvider\u003e{children}\u003c/CircadianProvider\u003e\n    \u003c/\u003e\n  );\n}\n```\n\n\u003e `CircadianScript` prevents theme flash by setting the initial phase before hydration.\n\n---\n\n## Next.js\n\n### App Router\n\n```tsx\n// app/layout.tsx\nimport \"./globals.css\";\nimport { CircadianProvider, CircadianScript } from \"@shiftbloom-studio/circadian-ui\";\n\nexport default function RootLayout({ children }: { children: React.ReactNode }) {\n  return (\n    \u003chtml lang=\"en\"\u003e\n      \u003cbody\u003e\n        \u003cCircadianScript /\u003e\n        \u003cCircadianProvider\u003e{children}\u003c/CircadianProvider\u003e\n      \u003c/body\u003e\n    \u003c/html\u003e\n  );\n}\n```\n\n### Pages Router\n\n```tsx\n// pages/_document.tsx\nimport Document, { Head, Html, Main, NextScript } from \"next/document\";\nimport { CircadianScript } from \"@shiftbloom-studio/circadian-ui\";\n\nexport default class MyDocument extends Document {\n  render() {\n    return (\n      \u003cHtml lang=\"en\"\u003e\n        \u003cHead\u003e\n          \u003cCircadianScript /\u003e\n        \u003c/Head\u003e\n        \u003cbody\u003e\n          \u003cMain /\u003e\n          \u003cNextScript /\u003e\n        \u003c/body\u003e\n      \u003c/Html\u003e\n    );\n  }\n}\n```\n\n---\n\n## Vite / CSR apps\n\n```tsx\nimport { createRoot } from \"react-dom/client\";\nimport { CircadianProvider, CircadianScript } from \"@shiftbloom-studio/circadian-ui\";\nimport App from \"./App\";\n\ncreateRoot(document.getElementById(\"root\")!).render(\n  \u003c\u003e\n    \u003cCircadianScript /\u003e\n    \u003cCircadianProvider\u003e\n      \u003cApp /\u003e\n    \u003c/CircadianProvider\u003e\n  \u003c/\u003e\n);\n```\n\n---\n\n## Tailwind integration\n\n### Option A — CSS variables (recommended)\n\n```ts\n// tailwind.config.ts\nimport type { Config } from \"tailwindcss\";\n\nconst config: Config = {\n  content: [\"./app/**/*.{ts,tsx}\", \"./components/**/*.{ts,tsx}\"],\n  theme: {\n    extend: {\n      colors: {\n        background: \"hsl(var(--cui-bg) / \u003calpha-value\u003e)\",\n        foreground: \"hsl(var(--cui-fg) / \u003calpha-value\u003e)\",\n        muted: \"hsl(var(--cui-muted) / \u003calpha-value\u003e)\",\n        \"muted-foreground\": \"hsl(var(--cui-muted-fg) / \u003calpha-value\u003e)\",\n        card: \"hsl(var(--cui-card) / \u003calpha-value\u003e)\",\n        \"card-foreground\": \"hsl(var(--cui-card-fg) / \u003calpha-value\u003e)\",\n        border: \"hsl(var(--cui-border) / \u003calpha-value\u003e)\",\n        ring: \"hsl(var(--cui-ring) / \u003calpha-value\u003e)\",\n        accent: \"hsl(var(--cui-accent) / \u003calpha-value\u003e)\",\n        \"accent-foreground\": \"hsl(var(--cui-accent-fg) / \u003calpha-value\u003e)\",\n        destructive: \"hsl(var(--cui-destructive) / \u003calpha-value\u003e)\",\n        \"destructive-foreground\": \"hsl(var(--cui-destructive-fg) / \u003calpha-value\u003e)\"\n      }\n    }\n  }\n};\n\nexport default config;\n```\n\n### Option B — Preset + Plugin\n\n```ts\n// tailwind.config.ts\nimport type { Config } from \"tailwindcss\";\nimport plugin from \"tailwindcss/plugin\";\nimport { circadianPlugin, circadianTailwindPreset } from \"@shiftbloom-studio/circadian-ui\";\n\nconst config: Config = {\n  presets: [circadianTailwindPreset()],\n  plugins: [plugin(circadianPlugin())]\n};\n\nexport default config;\n```\n\n---\n\n## Configuration examples\n\n### 1) Custom schedule windows\n\n```tsx\n\u003cCircadianProvider\n  config={{\n    schedule: {\n      dawn: { start: \"06:00\", end: \"09:00\" },\n      day: { start: \"09:00\", end: \"18:00\" },\n      dusk: { start: \"18:00\", end: \"22:00\" },\n      night: { start: \"22:00\", end: \"06:00\" }\n    }\n  }}\n\u003e\n  {children}\n\u003c/CircadianProvider\u003e\n```\n\n### 2) Sun‑aware schedule\n\n```ts\nimport type { SunTimesProvider } from \"@shiftbloom-studio/circadian-ui\";\n\nconst provider: SunTimesProvider = (date) =\u003e {\n  return {\n    sunrise: new Date(date.getFullYear(), date.getMonth(), date.getDate(), 6, 12),\n    sunset: new Date(date.getFullYear(), date.getMonth(), date.getDate(), 19, 48)\n  };\n};\n\n\u003cCircadianProvider config={{ mode: \"sun\", sunTimesProvider: provider }} /\u003e;\n```\n\n### 3) Auto mode (recommended)\n\n```tsx\n\u003cCircadianProvider config={{ mode: \"auto\", sunTimesProvider: provider }} /\u003e\n```\n\nIf sun data is available, it uses `sun`; otherwise it falls back to `time`.\n\n### 4) Manual override UI\n\n```tsx\nimport { useCircadian } from \"@shiftbloom-studio/circadian-ui\";\n\nconst ModeToggle = () =\u003e {\n  const { mode, resolvedMode, setMode, setPhaseOverride } = useCircadian();\n  return (\n    \u003cdiv\u003e\n      \u003cbutton onClick={() =\u003e setMode(\"auto\")}\u003eAuto\u003c/button\u003e\n      \u003cbutton onClick={() =\u003e setPhaseOverride(\"night\")}\u003eNight\u003c/button\u003e\n      \u003cp\u003eRequested: {mode}\u003c/p\u003e\n      \u003cp\u003eResolved: {resolvedMode}\u003c/p\u003e\n    \u003c/div\u003e\n  );\n};\n```\n\n### 5) Initial phase hint (SSR)\n\n```tsx\n\u003cCircadianScript config={{ initialPhase: \"night\" }} /\u003e\n```\n\n### 6) Disable persistence\n\n```tsx\n\u003cCircadianProvider config={{ persist: false }} /\u003e\n```\n\n### 7) Strict contrast tuning\n\n```tsx\n\u003cCircadianProvider\n  config={{\n    accessibility: {\n      enforceContrast: true,\n      minimumRatio: 7\n    }\n  }}\n/\u003e\n```\n\n---\n\n## Design tokens\n\n| Token                  | CSS Variable           |\n| ---------------------- | ---------------------- |\n| Background             | `--cui-bg`             |\n| Foreground             | `--cui-fg`             |\n| Muted                  | `--cui-muted`          |\n| Muted Foreground       | `--cui-muted-fg`       |\n| Card                   | `--cui-card`           |\n| Card Foreground        | `--cui-card-fg`        |\n| Border                 | `--cui-border`         |\n| Ring                   | `--cui-ring`           |\n| Accent                 | `--cui-accent`         |\n| Accent Foreground      | `--cui-accent-fg`      |\n| Destructive            | `--cui-destructive`    |\n| Destructive Foreground | `--cui-destructive-fg` |\n\n---\n\n## API reference\n\n### React\n\n- `CircadianProvider`\n  - Props: `{ config?: CircadianConfig; children: React.ReactNode }`\n  - Applies phase + tokens to the document root (or body).\n- `useCircadian()`\n  - Returns `{ phase, mode, resolvedMode, setMode, setPhaseOverride, clearOverride, tokens, isAuto, nextChangeAt }`.\n- `useCircadianTokens()`\n  - Returns `{ tokens, cssVars, applyToStyle }` for inline usage.\n- `CircadianScript`\n  - Inline script component to prevent flash before hydration.\n\n### Core utilities\n\n- `getPhaseFromTime(date, schedule)`\n- `getPhaseFromSunTimes(date, sunTimes, options)`\n- `computeNextTransition(date, schedule)`\n- `ensureContrast(tokens, options)`\n- `resolveMode(userMode, systemPrefs, config)`\n- `createInlineScript(config)`\n\n### Tailwind\n\n- `circadianTailwindPreset()`\n- `circadianPlugin()` (wrap with `tailwindcss/plugin`)\n\n---\n\n## Configuration schema\n\n```ts\ninterface CircadianConfig {\n  schedule?: Partial\u003cCircadianSchedule\u003e;\n  tokens?: Partial\u003cRecord\u003cPhase, Partial\u003cCircadianTokens\u003e\u003e\u003e;\n  mode?: \"time\" | \"sun\" | \"manual\" | \"auto\";\n  sunTimesProvider?: SunTimesProvider;\n  sunSchedule?: Partial\u003cSunScheduleOptions\u003e;\n  initialPhase?: Phase;\n  persist?: boolean;\n  storageKey?: string;\n  accessibility?: Partial\u003cAccessibilityOptions\u003e;\n  system?: Partial\u003cSystemPreferenceOptions\u003e;\n  colorSchemeBias?: Partial\u003cColorSchemeBias\u003e;\n  transition?: Partial\u003cTransitionOptions\u003e;\n  setAttributeOn?: \"html\" | \"body\";\n}\n```\n\n---\n\n## Accessibility notes\n\nCircadian 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`.\n\n---\n\n## Development\n\n```bash\nnpm ci\nnpm run lint\nnpm run typecheck\nnpm run test\nnpm run build\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshiftbloom-studio%2Fcircadian-ui","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fshiftbloom-studio%2Fcircadian-ui","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshiftbloom-studio%2Fcircadian-ui/lists"}