Preparing pages…Download Markdown

Registry Docs

Installable components, blocks, themes, and reference material for Circle Health products.

Registry

The Circle Health Registry serves installable components for Circle Health products through the shadcn CLI. Consumers copy components into their own codebase, own the generated files, and can edit them locally.

The Registry has four layers. Components are small primitives. Blocks combine them into product modules. Utilities provide behavior without a UI workflow. Themes provide the tokens that style all three.

Where to go

PageUse it for
InstallConfigure a consumer and install its first item.
ConceptsRegistry ownership, namespaces, dependencies, and generated output.
ComponentsActions, form controls, navigation, layout, and content primitives.
BlocksFlows, commerce, booking, media, reporting, and patient-results patterns.
UtilitiesAnalytics helpers and browser tools with no UI workflow attached.
ThemesThe token sets available to Circle Health surfaces.
Sample DataSynthetic PDFs and brands for development and demos.
SkillsCopy-review and link-check skills.

Source of truth

Authored React source lives under packages/ui and registry/. The registry build writes shadcn-compatible JSON to apps/docs/public. Do not hand-edit generated registry JSON.

The documentation and public payloads ship from the same host: registry.circle.health.

Install

The Circle Health Registry distributes source through the shadcn CLI. The CLI downloads source into the consumer; it does not add a runtime package.

1. Start with a shadcn project

The consumer needs a valid components.json. If it does not have one yet, run:

pnpm dlx shadcn@latest init

2. Add the registries

Add the Circle Health namespaces to components.json:

{ "$schema": "https://ui.shadcn.com/schema.json", "registries": { "@circle-ui": "https://registry.circle.health/circle-ui/{name}.json", "@blocks": "https://registry.circle.health/blocks/{name}.json", "@utilities": "https://registry.circle.health/utilities/{name}.json", "@i18n": "https://registry.circle.health/i18n/{name}.json", "@skills": "https://registry.circle.health/skills/{name}.json" } }

@circle-ui contains primitives. @blocks contains product modules. @utilities contains behavior with no UI workflow attached. @i18n contains optional locale dictionaries referenced by those modules. @skills contains agent workflows installed under .agents/skills.

3. Install the base style

Install the shared tokens and defaults once per consumer:

pnpm dlx shadcn@latest add @circle-ui/circle

4. Add what the product needs

Install items by namespace and name:

pnpm dlx shadcn@latest add @circle-ui/button pnpm dlx shadcn@latest add @blocks/booking-timeline pnpm dlx shadcn@latest add @utilities/analytics

The CLI resolves registry dependencies and writes the files into the consumer. Review those files like any other source change.

Direct URL installation

When a consumer cannot edit components.json, install from the public JSON URL:

pnpm dlx shadcn@latest add https://registry.circle.health/circle-ui/button.json

Next

Use Components for primitives, Blocks for assembled product patterns, Utilities for behavior-only helpers, and Concepts for the ownership and dependency model.

Concepts

Installed source

The Circle Health Registry distributes source rather than a runtime component package. Installing an item copies files into the consumer. The consumer then owns those files and can change them without waiting for a central package release.

Reinstalling an item may replace local changes. Inspect the diff before accepting an update.

Components

Components are low-level UI with no product workflow attached. Each registry entry maps to one source module and uses the @circle-ui namespace.

Blocks

Blocks are product modules assembled from components. A block may pull in private hooks, types, or serialization helpers, but those implementation files are not separate catalog choices.

Install one focused block for one product capability. Use an umbrella entry only when the capability is intentionally shipped as a coordinated family.

Utilities

Utilities provide behavior without a visual primitive or product workflow. They use the @utilities namespace so consumers can distinguish instrumentation and browser helpers from UI blocks.

Localized copy

Optional locale dictionaries use the @i18n namespace. They stay separate from UI source so consumers own translation loading and locale boundaries. The owning block documents the relevant dictionary.

Themes

Themes are CSS-variable sets. Components and blocks refer to those variables rather than hard-coded product colors, so a consumer can change a surface without rewriting component logic.

Dependencies

Each registry item declares two dependency kinds:

KindResolution
Registry dependencyThe shadcn CLI installs another Registry item.
npm dependencyThe consumer’s package manager installs a third-party package.

Registry dependencies use their full namespace in generated JSON. The docs show both dependency kinds on every reference page.

Authoring and generated output

Authoring source lives in packages/ui, packages/styles, and registry. Configuration lives in config/registry. pnpm registry:build produces the public JSON under apps/docs/public.

Treat generated JSON as build output. Change the authoring source or registry configuration, rebuild, and verify the generated diff.

Technical setup

This repository is a pnpm workspace managed by Turborepo. It builds two surfaces from the same source: the Nextra documentation site and the shadcn-compatible JSON consumed by Circle Health applications.

Manifest-backed stack

One workspace builds the catalog and the files it documents.

Package versions below come directly from the workspace manifests. The lockfile fixes the resolved dependency graph.

System boundary

The Registry distributes source, not a runtime UI package. @circle/ui is the private authoring package inside this workspace. Consumers use the shadcn CLI, receive files in their own source tree, and own those files from then on.

BoundaryResponsibility
AuthoringReact modules, CSS tokens, localized copy, skills, and item metadata.
RegistryImport rewriting, dependency discovery, targets, and item manifests.
DocumentationCatalog navigation, previews, usage, sample data, and public payloads.
ConsumerInstalled source, application integration, and later local changes.

Build pipeline

Author
Source and configuration
packages · skills · config
Compile
Registry manifests
registry/berlin · generated
Publish
Static JSON payloads
apps/docs/public
Consume
Application-owned source
shadcn writes into the app
Documentation branch: generated item metadata + docs config + changelogdocs:sync → MDX catalog

1. Author

The durable sources are:

Every root component must have one explicit registry item. An entry exported from packages/ui/src/index.ts can also receive a live documentation preview.

2. Compile

pnpm registry:sync runs scripts/registry/build.mjs. The script:

  1. reads authored source and registry configuration;
  2. maps each source module to one registry item;
  3. rewrites local imports to namespaced registry paths;
  4. derives npm and registry dependencies from imports;
  5. writes transformed source to registry/berlin; and
  6. writes manifests and catalog metadata to config/registry/generated and registry.json.

Explicit dependencies and file targets in items.json supplement values that cannot be derived from imports.

3. Publish

pnpm registry:build performs the registry sync, then runs shadcn build once per namespace. The resulting JSON is written below apps/docs/public:

/circle-ui/{name}.json /blocks/{name}.json /utilities/{name}.json /i18n/{name}.json /skills/{name}.json

The documentation application and these static payloads are served from registry.circle.health.

4. Generate documentation

pnpm docs:sync combines generated item metadata with docs configuration. It produces catalog pages, sidebar maps, skill pages, the CircleOS PII-mask page, and the rendered changelog.

Handwritten pages such as this one explain durable concepts. Item reference pages are generated so their install commands and dependencies stay aligned with the public payload. An item listed in its namespace’s authoredItems configuration keeps its handwritten page while still using live RegistryItem sections for installation and dependency data.

Workspace map

PathOwns
packages/uiInstallable React and TypeScript source.
packages/stylesBase CSS and Circle Health design tokens.
config/registryRegistry definitions plus generated manifests.
config/docsCatalog information architecture.
registry/berlinTransformed source passed to the shadcn builder.
apps/docs/contentHandwritten and generated Nextra MDX.
apps/docs/publicPublic registry JSON, fonts, brand assets, and synthetic data.
scripts/registryRegistry compilation and public-payload builds.
scripts/docsDocumentation synchronization.
scripts/sample-dataSynthetic lab-report and intake-form generation.

Documentation runtime

The docs app uses the Next.js App Router with Nextra. The catch-all route at apps/docs/app/[[...mdxPath]]/page.tsx imports pages from apps/docs/content. apps/docs/content/_meta.tsx owns the top-level navigation.

The docs package runs registry and docs generation in both predev and prebuild. A local or production Next.js build therefore uses current generated artifacts instead of whatever happened to be on disk beforehand.

The root layout marks the site noindex, nofollow, and noarchive. That is a crawler policy, not access control.

Generated boundaries

Change the source on the left, then regenerate the output on the right.

Source of truthGenerated output
packages/ui, packages/styles, skillsregistry/berlin
config/registry/*.json and authored sourceconfig/registry/generated, registry.json
Generated namespace manifestsRegistry JSON under apps/docs/public
Generated item metadata + config/docsComponent, block, utility, and skill catalog pages
CHANGELOG.mdapps/docs/content/changelog.mdx
scripts/circle-os/pii-mask.jsapps/docs/content/utilities/pii-mask.mdx
scripts/sample-data configuration and sourcesPDFs and manifests under apps/docs/public/sample-data

Do not hand-edit generated output. The relevant build removes and recreates managed directories.

Local workflow

Start the docs application:

pnpm install pnpm dev:docs

After changing registry source or configuration:

pnpm registry:build pnpm docs:sync pnpm typecheck pnpm build

pnpm build is the production gate. Turborepo type-checks the private UI package, rebuilds registry and docs artifacts through the docs package hooks, and runs the Next.js production build.

Failure modes

Themes

Themes are CSS-variable sets consumed by components and blocks. Install the base style first, then choose the token set that matches the product surface.

The catalog below is generated from config/registry/styles.json.

Berlin

berlin

The default Circle Health product palette with warm sand surfaces and blue actions.

Supporting tokens (69)

Typography

--font-monoui-monospace, Menlo, Monaco, "Cascadia Mono", "Segoe UI Mono", "Roboto Mono", "Oxygen Mono", "Ubuntu Monospace", "Source Code Pro", "Fira Mono", "Droid Sans Mono", "Courier New", monospace
--font-sans"Be Vietnam Pro", "Segoe UI", Tahoma, Geneva, Verdana, sans-serif
--font-tiempos"Tiempos Text", Georgia, "Times New Roman", serif
--text-body-140.875rem
--text-body-14--font-weight400
--text-body-14--letter-spacing-0.01em
--text-body-14--line-height1.5rem
--text-body-14-medium0.875rem
--text-body-14-medium--font-weight500
--text-body-14-medium--letter-spacing-0.02em
--text-body-14-medium--line-height1.5rem
--text-body-161rem
--text-body-16--font-weight400
--text-body-16--letter-spacing-0.01em
--text-body-16--line-height1.5rem
--text-body-16-medium1rem
--text-body-16-medium--font-weight500
--text-body-16-medium--letter-spacing-0.02em
--text-body-16-medium--line-height1.5rem
--text-body-201.25rem
--text-body-20--font-weight400
--text-body-20--letter-spacing-0.01em
--text-body-20--line-height2rem
--text-body-20-medium1.25rem
--text-body-20-medium--font-weight500
--text-body-20-medium--letter-spacing-0.02em
--text-body-20-medium--line-height2rem
--text-body-28-medium1.75rem
--text-body-28-medium--font-weight500
--text-body-28-medium--letter-spacing-0.03em
--text-body-28-medium--line-height2.25rem
--text-display-281.75rem
--text-display-28--font-weight400
--text-display-28--letter-spacing-0.02em
--text-display-28--line-height2.25rem
--text-display-362.25rem
--text-display-36--font-weight400
--text-display-36--letter-spacing-0.02em
--text-display-36--line-height3rem
--text-display-483rem
--text-display-48--font-weight400
--text-display-48--letter-spacing-0.02em
--text-display-48--line-height4rem
--text-display-563.5rem
--text-display-56--font-weight400
--text-display-56--letter-spacing-0.02em
--text-display-56--line-height4.5rem
--text-heading0.75rem
--text-heading--font-weight600
--text-heading--letter-spacing0.12em
--text-heading--line-height0.75rem
--text-label0.6875rem
--text-label--font-weight600
--text-label--letter-spacing0.06em
--text-label--line-height0.75rem
--text-small0.6875rem
--text-small--font-weight400
--text-small--letter-spacing0
--text-small--line-height1rem

Shape

--border-radius12px
--radius0.75rem
--radius-control1rem
--radius-dialog1rem
--radius-field0.5rem
--radius-mediavar(--border-radius)
--shadow-elevated0 20px 60px rgba(0, 0, 0, 0.16)
--shadow-focus0 0 0 2px var(--White), 0 0 0 4px var(--Black)
--shadow-soft0 4px 12px rgba(0, 0, 0, 0.02), 0 1px 4px rgba(0, 0, 0, 0.04)

Misc

--max-width1100px

Linen

linen

A brighter editorial variant with softer neutrals and a greener accent.

Supporting tokens (69)

Typography

--font-monoui-monospace, Menlo, Monaco, "Cascadia Mono", "Segoe UI Mono", "Roboto Mono", "Oxygen Mono", "Ubuntu Monospace", "Source Code Pro", "Fira Mono", "Droid Sans Mono", "Courier New", monospace
--font-sans"Be Vietnam Pro", "Segoe UI", Tahoma, Geneva, Verdana, sans-serif
--font-tiempos"Tiempos Text", Georgia, "Times New Roman", serif
--text-body-140.875rem
--text-body-14--font-weight400
--text-body-14--letter-spacing-0.01em
--text-body-14--line-height1.5rem
--text-body-14-medium0.875rem
--text-body-14-medium--font-weight500
--text-body-14-medium--letter-spacing-0.02em
--text-body-14-medium--line-height1.5rem
--text-body-161rem
--text-body-16--font-weight400
--text-body-16--letter-spacing-0.01em
--text-body-16--line-height1.5rem
--text-body-16-medium1rem
--text-body-16-medium--font-weight500
--text-body-16-medium--letter-spacing-0.02em
--text-body-16-medium--line-height1.5rem
--text-body-201.25rem
--text-body-20--font-weight400
--text-body-20--letter-spacing-0.01em
--text-body-20--line-height2rem
--text-body-20-medium1.25rem
--text-body-20-medium--font-weight500
--text-body-20-medium--letter-spacing-0.02em
--text-body-20-medium--line-height2rem
--text-body-28-medium1.75rem
--text-body-28-medium--font-weight500
--text-body-28-medium--letter-spacing-0.03em
--text-body-28-medium--line-height2.25rem
--text-display-281.75rem
--text-display-28--font-weight400
--text-display-28--letter-spacing-0.02em
--text-display-28--line-height2.25rem
--text-display-362.25rem
--text-display-36--font-weight400
--text-display-36--letter-spacing-0.02em
--text-display-36--line-height3rem
--text-display-483rem
--text-display-48--font-weight400
--text-display-48--letter-spacing-0.02em
--text-display-48--line-height4rem
--text-display-563.5rem
--text-display-56--font-weight400
--text-display-56--letter-spacing-0.02em
--text-display-56--line-height4.5rem
--text-heading0.75rem
--text-heading--font-weight600
--text-heading--letter-spacing0.12em
--text-heading--line-height0.75rem
--text-label0.6875rem
--text-label--font-weight600
--text-label--letter-spacing0.06em
--text-label--line-height0.75rem
--text-small0.6875rem
--text-small--font-weight400
--text-small--letter-spacing0
--text-small--line-height1rem

Shape

--border-radius12px
--radius0.75rem
--radius-control1rem
--radius-dialog1rem
--radius-field0.5rem
--radius-mediavar(--border-radius)
--shadow-elevated0 20px 60px rgba(0, 0, 0, 0.16)
--shadow-focus0 0 0 2px var(--White), 0 0 0 4px var(--Black)
--shadow-soft0 4px 12px rgba(0, 0, 0, 0.02), 0 1px 4px rgba(0, 0, 0, 0.04)

Misc

--max-width1100px

Graphite

graphite

A cooler, higher-contrast operating palette for dense internal tooling surfaces.

Supporting tokens (69)

Typography

--font-monoui-monospace, Menlo, Monaco, "Cascadia Mono", "Segoe UI Mono", "Roboto Mono", "Oxygen Mono", "Ubuntu Monospace", "Source Code Pro", "Fira Mono", "Droid Sans Mono", "Courier New", monospace
--font-sans"Be Vietnam Pro", "Segoe UI", Tahoma, Geneva, Verdana, sans-serif
--font-tiempos"Tiempos Text", Georgia, "Times New Roman", serif
--text-body-140.875rem
--text-body-14--font-weight400
--text-body-14--letter-spacing-0.01em
--text-body-14--line-height1.5rem
--text-body-14-medium0.875rem
--text-body-14-medium--font-weight500
--text-body-14-medium--letter-spacing-0.02em
--text-body-14-medium--line-height1.5rem
--text-body-161rem
--text-body-16--font-weight400
--text-body-16--letter-spacing-0.01em
--text-body-16--line-height1.5rem
--text-body-16-medium1rem
--text-body-16-medium--font-weight500
--text-body-16-medium--letter-spacing-0.02em
--text-body-16-medium--line-height1.5rem
--text-body-201.25rem
--text-body-20--font-weight400
--text-body-20--letter-spacing-0.01em
--text-body-20--line-height2rem
--text-body-20-medium1.25rem
--text-body-20-medium--font-weight500
--text-body-20-medium--letter-spacing-0.02em
--text-body-20-medium--line-height2rem
--text-body-28-medium1.75rem
--text-body-28-medium--font-weight500
--text-body-28-medium--letter-spacing-0.03em
--text-body-28-medium--line-height2.25rem
--text-display-281.75rem
--text-display-28--font-weight400
--text-display-28--letter-spacing-0.02em
--text-display-28--line-height2.25rem
--text-display-362.25rem
--text-display-36--font-weight400
--text-display-36--letter-spacing-0.02em
--text-display-36--line-height3rem
--text-display-483rem
--text-display-48--font-weight400
--text-display-48--letter-spacing-0.02em
--text-display-48--line-height4rem
--text-display-563.5rem
--text-display-56--font-weight400
--text-display-56--letter-spacing-0.02em
--text-display-56--line-height4.5rem
--text-heading0.75rem
--text-heading--font-weight600
--text-heading--letter-spacing0.12em
--text-heading--line-height0.75rem
--text-label0.6875rem
--text-label--font-weight600
--text-label--letter-spacing0.06em
--text-label--line-height0.75rem
--text-small0.6875rem
--text-small--font-weight400
--text-small--letter-spacing0
--text-small--line-height1rem

Shape

--border-radius12px
--radius0.75rem
--radius-control1rem
--radius-dialog1rem
--radius-field0.5rem
--radius-mediavar(--border-radius)
--shadow-elevated0 20px 60px rgba(0, 0, 0, 0.16)
--shadow-focus0 0 0 2px var(--White), 0 0 0 4px var(--Black)
--shadow-soft0 4px 12px rgba(0, 0, 0, 0.02), 0 1px 4px rgba(0, 0, 0, 0.04)

Misc

--max-width1100px

Skills

Installable AI skills for common Circle Health review and QA tasks.

Copy Review

Review customer-facing copy for healing promises, language issues, and formal Sie phrasing.

Install

Terminal
pnpm dlx shadcn@latest add @skills/copy-review

Skill

Copy Review SKILL.md
---
name: copy-review
description: Review customer-facing Circle Health copy for healing promises, German or English language issues, and formal Sie phrasing. Use before publishing UI text, websites, emails, notifications, translations, or other user-facing copy.
---
 
# Copy review
 
Review the supplied copy and:
 
- Flag cures, guaranteed outcomes, or specific medical results. Suggest cautious alternatives such as "kann unterstützen", "zielt darauf ab", or "may help support".
- Correct spelling, grammar, and awkward phrasing in German or English.
- Replace formal German "Sie", "Ihnen", and "Ihr" with warm, informal "Du", "dir", and "dein" forms.
 
For every issue, quote the original text, name the category, and provide a replacement. If no issues remain, confirm that the copy is ready.

Changelog

Release history for The Circle Health Registry.

v2.0.0 — Canonical Registry

Added

Changed

Removed

v1.0.0 — Initial Release

The first public release of The Circle Health Registry at registry.circle.health.

Added

Pages

CMS (Registry Content System)

Core UI Components (30 primitives)

GroupItems
Actions & FeedbackAlert, Badge, Button, Progress Bar, Spinner, Tag
Inputs & FormsCheckbox, Switch, Date Picker, Dropdown, Form, Phone Input, Select With Input, TextArea, TextBox
Navigation & DisclosureBack Button, Link, Sheet, Tabs, Text Link
Layout & ContentCard, Container, Empty, Flex, Spacer, Text, Text With Icon, Timeline

Blocks (62 compositions)

GroupItems
Flow PatternsFloating Flow Actions, Flow, Flow Actions, Flow Button, Flow Head, Flow Options, Flow Progress, Top Bar
CommerceCart Line Item, Commerce, Discount Input, Input Discount, Pricing Table, Pricing Table Row, Product Card, Product Item, Quantity Adjustor
MediaHero Gallery, Image Lightbox, Image With Lightbox, Lightbox, Media, Media Slide, Testimonials Carousel, Text Only Testimonial, Video
BookingBooking Timeline, Booking Timeline Item, Booking Timeline Indicator, Booking Timeline Progress Dot, Booking Timeline Progress Separator, Booking Timeline Step, Booking Timeline Step Basics, Booking Timeline Step Content Notes, Booking Timeline Step Content Directions, Booking Timeline Step Content Todo List, Booking Timeline Todo Status Indicator
Address & SupportAddress, Address Input, Auto Address Inputs, General, Map, Manual Address Inputs, Rating, Support
ComplianceCookie Consent
Reporting PrimitivesMetric Range Card, Metric Donut Card, Metric Split Card, Metric Stat Card, Text Columns Card
Patient ResultsPatient Results Blocks (umbrella), Segment Muscle Analysis, Diagnostic Report Header, Diagnostic Report Footer, Diagnostic Report Intro, Diagnostic Report Page Shell, Patient Results Editor, Patient Results Report, Patient Results PDF, VO2 Max Report, VNS Report, Body Composition Report

I18n Copy Packs

Themes

Infrastructure

AGENTS.MD

General guidance for agents working across Circle Health repositories. More specific instructions closer to the code being changed take precedence.

Sources of truth

Change discipline

Structure and naming

Types and boundaries

Interface work

Copy AGENTS.md

# Agent guidance Use these rules throughout this repository. More specific instructions closer to the code being changed take precedence. ## Sources of truth - Read the relevant code, configuration, and project documentation before editing. Do not rely on remembered framework behaviour when the installed version can answer the question. - For Next.js work, read the relevant guide in `node_modules/next/dist/docs/` before writing code. The installed version may contain breaking API, convention, or file-structure changes. Heed deprecation notices. - When a task references Figma or another design source, inspect that source and match its copy, layout, styles, and assets. Do not invent missing design decisions. - Preserve explicit product constraints such as privacy, indexing, security, and public visibility unless the task changes them. ## Change discipline - Keep changes small, local, and directly related to the feature or bug. - Preserve unrelated work in a dirty worktree. - Reuse existing primitives, tokens, and patterns before introducing a new abstraction. - Verify changes with the narrowest useful tests, type checks, lint checks, or build steps for the affected area. ## Structure and naming - Prefer cute, neat names and logical domain boundaries. Use `commerce/readiness/medusa.ts`, not `commerce-readiness.medusa.ts`. - Keep one clear responsibility per module and place it beside the domain that owns it. - Prefer functional factories and functions over classes when they make dependency injection and testing simpler. - Quote route-segment paths in shell commands so characters such as `[]` and `()` are not expanded by the shell. ## Types and boundaries - Do not introduce `any`, `as any`, `@ts-ignore`, or broad double casts when a practical typed alternative exists. - Prefer generated types, schema-inferred types, `unknown` with narrowing, or a small explicit interface. - If an escape hatch is unavoidable, keep it at the untyped boundary and explain why it is needed. - Keep input validation and authorization checks close to the API, data, or service boundary they protect. ## Interface work - In localized surfaces, keep user-facing copy in the localization system. Preserve German as a first-class locale, update both English and German messages, and prefer message interpolation over string concatenation. - Use the existing design system and component primitives instead of ad-hoc colours, typography, spacing, or controls. - For technologies, AI models, and integrations, use the service's `domain.com/favicon.ico` when a recognizable brand mark is needed and no canonical local asset exists.

Components

Low-level UI with no product workflow attached.

Alert

Status callout for inline feedback.

Loading preview…

Install

Terminal
pnpm dlx shadcn@latest add @circle-ui/alert

Use

TSX
import { AlertCard } from "@circle/ui";

Badge

Compact status or category label.

Loading preview…

Install

Terminal
pnpm dlx shadcn@latest add @circle-ui/badge

Use

TSX
import { Badge } from "@circle/ui";

Button

Primary, secondary, and text actions.

Loading preview…

Install

Terminal
pnpm dlx shadcn@latest add @circle-ui/button

Use

TSX
import { Button } from "@circle/ui";

Dependencies

KindPackages
npm@radix-ui/react-slot, class-variance-authority

Progress Bar

Determinate progress track and indicator.

Loading preview…

Install

Terminal
pnpm dlx shadcn@latest add @circle-ui/progress-bar

Use

TSX
import { ProgressBar } from "@circle/ui";

Rating

Rating value, stars, and review count.

Loading preview…

Install

Terminal
pnpm dlx shadcn@latest add @circle-ui/rating

Use

TSX
import { Rating } from "@circle/ui";

Spinner

Indeterminate loading indicator.

Loading preview…

Install

Terminal
pnpm dlx shadcn@latest add @circle-ui/spinner

Use

TSX
import { Spinner } from "@circle/ui";

Tag

Removable or static metadata label.

Loading preview…

Install

Terminal
pnpm dlx shadcn@latest add @circle-ui/tag

Use

TSX
import { Tag } from "@circle/ui";

Checkbox

Controlled or uncontrolled checkbox.

Loading preview…

Install

Terminal
pnpm dlx shadcn@latest add @circle-ui/checkbox

Use

TSX
import { Checkbox } from "@circle/ui";

Dependencies

KindPackages
npm@radix-ui/react-checkbox

Date Picker

Single-date calendar picker.

Loading preview…

Install

Terminal
pnpm dlx shadcn@latest add @circle-ui/date-picker

Use

TSX
import { DatePicker } from "@circle/ui";

Dependencies

KindPackages
npmreact-day-picker

Form Fields

Form state, text fields, text areas, selects, and phone input.

Loading preview…

Install

Terminal
pnpm dlx shadcn@latest add @circle-ui/form

Use

TSX
import { Form } from "@circle/ui";

Dependencies

KindPackages
npmreact-international-phone

Switch

Binary setting control.

Loading preview…

Install

Terminal
pnpm dlx shadcn@latest add @circle-ui/switch

Use

TSX
import { Switch } from "@circle/ui";

Dependencies

KindPackages
npm@radix-ui/react-switch

Back Button

Back control with label, icon, and chevron variants.

Loading preview…

Install

Terminal
pnpm dlx shadcn@latest add @circle-ui/back-button

Use

TSX
import { BackButton } from "@circle/ui";

Overlay Action

Accessible action placed over media or another visual surface.

Loading preview…

Install

Terminal
pnpm dlx shadcn@latest add @circle-ui/overlay-action

Use

TSX
import { OverlayAction } from "@circle/ui";

Dependencies

KindPackages
npmclass-variance-authority

Sheet

Modal sheet with trigger, overlay, content, and close controls.

Loading preview…

Install

Terminal
pnpm dlx shadcn@latest add @circle-ui/sheet

Use

TSX
import { Sheet } from "@circle/ui";

Dependencies

KindPackages
Registry@circle-ui/overlay-action
npmclass-variance-authority, vaul

Tabs

Tabbed navigation and content panels.

Loading preview…

Install

Terminal
pnpm dlx shadcn@latest add @circle-ui/tabs

Use

TSX
import { TabRoot } from "@circle/ui";

Dependencies

KindPackages
npm@radix-ui/react-tabs

Tooltip

Contextual help with default or custom triggers.

Loading preview…

Install

Terminal
pnpm dlx shadcn@latest add @circle-ui/tooltip

Use

TSX
import { Tooltip, TooltipWrapper } from "@circle/ui";

<Tooltip content="Reviewed before sharing." side="right" />

<TooltipWrapper content="More context" side="top">
  <button type="button">Details</button>
</TooltipWrapper>

Dependencies

KindPackages
npm@radix-ui/react-tooltip

Card

Surface, header, content, footer, and action slots.

Loading preview…

Install

Terminal
pnpm dlx shadcn@latest add @circle-ui/card

Use

TSX
import { Card } from "@circle/ui";

Container

Centered, width-constrained page container.

Loading preview…

Install

Terminal
pnpm dlx shadcn@latest add @circle-ui/container

Use

TSX
import { Container } from "@circle/ui";

Empty

Empty-state layouts with optional title and description.

Loading preview…

Install

Terminal
pnpm dlx shadcn@latest add @circle-ui/empty

Use

TSX
import { Empty } from "@circle/ui";

Dependencies

KindPackages
Registry@circle-ui/flex

Text

Product typography with semantic element options.

Loading preview…

Install

Terminal
pnpm dlx shadcn@latest add @circle-ui/text

Use

TSX
import { Text } from "@circle/ui";

Text With Icon

Inline label paired with an icon.

Loading preview…

Install

Terminal
pnpm dlx shadcn@latest add @circle-ui/text-with-icon

Use

TSX
import { TextWithIcon } from "@circle/ui";

Dependencies

KindPackages
Registry@circle-ui/flex

Timeline

Vertical sequence of connected steps.

Loading preview…

Install

Terminal
pnpm dlx shadcn@latest add @circle-ui/timeline

Use

TSX
import { Timeline } from "@circle/ui";

Dependencies

KindPackages
Registry@circle-ui/flex

Blocks

Product modules composed from components.

Flow

Shell, header, progress, choices, and actions for step-based journeys.

Loading preview…

Install

Terminal
pnpm dlx shadcn@latest add @blocks/flow

Use

TSX
import { FlowWrapper } from "@circle/ui";

Dependencies

KindPackages
Registry@circle-ui/button, @circle-ui/progress-bar

Floating Flow Actions

Pinned submit action with loading and disabled states.

Loading preview…

Install

Terminal
pnpm dlx shadcn@latest add @blocks/floating-flow-actions

Use

TSX
import { FloatingFlowActions } from "@circle/ui";

Dependencies

KindPackages
Registry@blocks/flow, @circle-ui/spinner

Top Bar

Back, close, and logo controls for flow headers.

Loading preview…

Install

Terminal
pnpm dlx shadcn@latest add @blocks/top-bar

Use

TSX
import { TopBar } from "@circle/ui";

Dependencies

KindPackages
Registry@blocks/flow

Fillout Flow

Schema-driven Fillout flow renderer with components from the circle-ui namespace and compatible submission payloads.

Install

Terminal
pnpm dlx shadcn@latest add @blocks/fillout-flow

Use

The built-in mapper covers common choice, text, email, phone, number, and date fields. Use a kind override or custom renderer for other Fillout field types.
TSX
import {
  FilloutFlow,
  createFilloutFlowDefinition,
  type FilloutFormMetadata,
} from "@circle/ui";

const flow = createFilloutFlowDefinition(form as FilloutFormMetadata, {
  steps: [
    { id: "goal", fields: ["kaRM"] },
    { id: "details", fields: ["h8ay", "goHS"] },
  ],
});

<FilloutFlow flow={flow} onSubmit={submitToFillout} />;

Setup

  1. Fetch Fillout form metadata through a server or safe API route.
  2. Group question ids into a flow definition.
  3. Post the resulting payload back to Fillout from the submit callback.

Works with

  • flowUse the lower-level shell when Fillout is not the schema source.

Dependencies

KindPackages
Registry@blocks/fillout-flow-schema, @blocks/fillout-flow-submission, @blocks/use-fillout-flow, @circle-ui/container, @circle-ui/date-picker, @circle-ui/form, @circle-ui/text

Commerce

Product, pricing, quantity, cart, and discount patterns.

Loading preview…

Install

Terminal
pnpm dlx shadcn@latest add @blocks/commerce

Use

TSX
import { ProductCard } from "@circle/ui";

Dependencies

KindPackages
Registry@circle-ui/badge, @circle-ui/button, @circle-ui/form

Booking Timeline

Ordered booking steps with progress, notes, directions, and tasks.

Loading preview…

Install

Terminal
pnpm dlx shadcn@latest add @blocks/booking-timeline

Use

TSX
import { BookingTimeline } from "@circle/ui";

Dependencies

KindPackages
Registry@blocks/map, @circle-ui/button

Media

Video, lightbox, gallery, carousel, and testimonial patterns.

Loading preview…

Install

Terminal
pnpm dlx shadcn@latest add @blocks/media

Use

TSX
import { Video } from "@circle/ui";

Dependencies

KindPackages
Registry@circle-ui/overlay-action

Map

Mapbox location preview with coordinate fallback.

Loading preview…

Install

Terminal
pnpm dlx shadcn@latest add @blocks/map

Use

TSX
import { Map } from "@circle/ui";

Address

Address search, manual entry, and mode switching.

Loading preview…

Install

Terminal
pnpm dlx shadcn@latest add @blocks/address

Use

TSX
import { AddressInput } from "@circle/ui";

Dependencies

KindPackages
Registry@circle-ui/form
npmmapbox-gl

Support

Support contact card with an avatar and link.

Loading preview…

Install

Terminal
pnpm dlx shadcn@latest add @blocks/support

Use

TSX
import { Support } from "@circle/ui";

Dependencies

KindPackages
Registry@circle-ui/link

Metric Donut Card

Circular metric card for compact progress and threshold summaries.

Loading preview…

Install

Terminal
pnpm dlx shadcn@latest add @blocks/metric-donut-card

Use

TSX
import { MetricDonutCard } from "@circle/ui";

Dependencies

KindPackages
Registry@blocks/reporting-types, @circle-ui/card

Metric Range Card

Range-based metric card for score bands, ticks, and highlighted target ranges.

Loading preview…

Install

Terminal
pnpm dlx shadcn@latest add @blocks/metric-range-card

Use

TSX
import { MetricRangeCard } from "@circle/ui";

Dependencies

KindPackages
Registry@blocks/metric-utils, @blocks/reporting-types, @circle-ui/card

Metric Split Card

Two-sided comparison card for paired values or before-and-after measurements.

Loading preview…

Install

Terminal
pnpm dlx shadcn@latest add @blocks/metric-split-card

Use

TSX
import { MetricSplitCard } from "@circle/ui";

Dependencies

KindPackages
Registry@blocks/metric-utils, @blocks/reporting-types, @circle-ui/card

Metric Stat Card

Single-value metric card with optional status labelling.

Loading preview…

Install

Terminal
pnpm dlx shadcn@latest add @blocks/metric-stat-card

Use

TSX
import { MetricStatCard } from "@circle/ui";

Dependencies

KindPackages
Registry@blocks/metric-utils, @blocks/reporting-types, @circle-ui/card

Text Columns Card

Neutral two-column text layout with independent headings and copy.

Loading preview…

Install

Terminal
pnpm dlx shadcn@latest add @blocks/text-columns-card

Use

TSX
import { TextColumnsCard } from "@circle/ui";

Dependencies

KindPackages
Registry@blocks/reporting-types

Segment Muscle Analysis

Composed muscle-distribution block with a figure image, hotspots, and surrounding metric cards.

Loading preview…

Install

Terminal
pnpm dlx shadcn@latest add @blocks/segment-muscle-analysis

Use

TSX
import { SegmentMuscleAnalysis } from "@circle/ui";

Dependencies

KindPackages
Registry@blocks/metric-donut-card, @blocks/patient-results-types, @circle-ui/card

Patient Results Editor

BlockNote editor with diagnostic blocks registered.

Loading preview…

Install

Terminal
pnpm dlx shadcn@latest add @blocks/patient-results-editor

Use

TSX
import { PatientResultsEditor } from "@circle/ui";

Document flow

  1. Seed the editor with a preset or saved BlockNote document.
  2. Persist the editor document as the source of truth.
  3. Pass that document to the browser or PDF renderer.

Works with

Dependencies

KindPackages
Registry@blocks/diagnostic-interpretation-columns, @blocks/metric-donut-card, @blocks/metric-range-card, @blocks/metric-split-card, @blocks/metric-stat-card, @blocks/segment-muscle-analysis, @circle-ui/button
npm@blocknote/core, @blocknote/shadcn

Patient Results Report

Preset-aware browser renderer for patient-results documents.

Loading preview…

Install

Terminal
pnpm dlx shadcn@latest add @blocks/patient-results-report

Use

TSX
import { PatientResultsReport } from "@circle/ui";

Works with

Dependencies

KindPackages
Registry@blocks/diagnostic-interpretation-columns, @blocks/diagnostic-report-intro, @blocks/diagnostic-report-page-shell, @blocks/metric-donut-card, @blocks/metric-range-card, @blocks/metric-split-card, @blocks/metric-stat-card, @blocks/patient-results-presets, @blocks/segment-muscle-analysis

Patient Results PDF

React-pdf renderer for patient-results documents.

Install

Terminal
pnpm dlx shadcn@latest add @blocks/patient-results-pdf

Use

This is an export module, not an editing surface.
TSX
import { pdf } from "@react-pdf/renderer";
import { PatientResultsPdfDocumentFromEditor } from "@/components/blocks/patient-results-pdf";

const blob = await pdf(
  <PatientResultsPdfDocumentFromEditor
    document={editor.document}
    presetId="vns-report"
  />,
).toBlob();

Works with

Dependencies

KindPackages
Registry@blocks/patient-results-presets, @blocks/patient-results-types

Patient Results Suite

Umbrella install for the editor, browser report, PDF, and presets.

Install

Terminal
pnpm dlx shadcn@latest add @blocks/patient-results-blocks

Dependencies

KindPackages
Registry@blocks/diagnostic-report-footer, @blocks/diagnostic-report-header, @blocks/diagnostic-report-intro, @blocks/diagnostic-report-page-shell, @blocks/metric-donut-card, @blocks/metric-range-card, @blocks/metric-split-card, @blocks/metric-stat-card, @blocks/patient-results-assets, @blocks/patient-results-editor, @blocks/patient-results-pdf, @blocks/patient-results-presets, @blocks/patient-results-report, @blocks/patient-results-serialization, @blocks/patient-results-types, @blocks/segment-muscle-analysis, @blocks/text-columns-card

Utilities

Installable helpers and browser tools with no UI workflow attached.

Nextra Export

Export a complete Nextra site as one Markdown file or a print-ready book. The PDF view renders the site’s real MDX and React components. Markdown components use a project-owned renderer registry.

Install

Terminal
pnpm dlx shadcn@latest add @utilities/nextra-export

The command writes the exporter below src/lib/nextra-export. It installs unified and the Remark packages used for MDX normalization. The target site must already use Nextra 4.

Load the documentation tree

Create one project-specific loader:

src/lib/docs-export.ts
import { collectNextraExportPages, loadNextraPageSources, } from "@/lib/nextra-export"; import { importPage } from "nextra/pages"; import { getPageMap } from "nextra/page-map"; export async function loadDocsExportPages() { const pages = collectNextraExportPages(await getPageMap()); return loadNextraPageSources(pages, importPage); }

Page order follows Nextra navigation. Set export: false in a page’s frontmatter to omit it.

Add the PDF route

Reserve /export/pdf inside the existing Nextra catch-all. Next 16 can let the root optional catch-all shadow a sibling page at the same URL.

app/[[...mdxPath]]/page.tsx
import { ExportBook } from "@/lib/nextra-export"; import { loadDocsExportPages } from "@/lib/docs-export"; async function PdfExportPage() { const pages = await loadDocsExportPages(); return ( <ExportBook title="My Docs" siteHref="https://docs.example.com" siteLabel="docs.example.com" pages={pages.map(({ Content, route, sectionPath, title }) => ({ content: <Content />, route, sectionPath, title, }))} /> ); } export default async function Page({ params }) { const { mdxPath } = await params; if (mdxPath?.join("/") === "export/pdf") return <PdfExportPage />; // Keep the site's existing importPage(...) implementation here. }

If the Nextra theme layout wraps the catch-all route, return children directly from that layout for export/pdf. This keeps the navbar and sidebar out of the book.

Add the button to the Nextra navbar:

import { ExportPdfButton } from "@/lib/nextra-export"; <Navbar logo={logo}> <ExportPdfButton /> </Navbar>;

The button opens the complete book and starts the browser print flow after its fonts and images settle. Choose Save as PDF to create one bundled file. The table of contents uses internal PDF jumps. Links inside exported pages are rewritten to canonical absolute URLs and printed in full after their labels, so they remain usable in strict PDF viewers and on paper. siteHref sets that canonical origin and adds the documentation link to the cover.

Add the Markdown route

Create a route handler outside the Nextra catch-all:

app/api/export/markdown/route.ts
import { exportMarkdownBundle } from "@/lib/nextra-export"; import { loadDocsExportPages } from "@/lib/docs-export"; export async function GET() { const markdown = await exportMarkdownBundle({ title: "My Docs", pages: await loadDocsExportPages(), siteUrl: "https://docs.example.com", }); return new Response(markdown, { headers: { "Content-Disposition": 'attachment; filename="my-docs.md"', "Content-Type": "text/markdown; charset=utf-8", }, }); }

Internal page links become anchors in the bundled document. Frontmatter and MDX module statements are removed.

Render custom MDX components

Pass a renderer for each project-specific component:

import type { ComponentRendererRegistry } from "@/lib/nextra-export"; export const components: ComponentRendererRegistry = { ProductCard: ({ attributes }) => `**${attributes.title}** — [Open product](${attributes.href})`, Diagram: async ({ attributes }) => `![${attributes.alt}](${await renderDiagramImage(attributes)})`, };

Renderers receive literal attributes, normalized Markdown children, and the current page. They may return text, tables, code, links, or generated image URLs. Unregistered components keep their children and produce a labeled fallback instead of disappearing.

Pass the registry as components to exportMarkdownBundle.

Analytics

Provider-neutral events and tag-manager bridge.

Install

Terminal
pnpm dlx shadcn@latest add @utilities/analytics

Use

TSX
import { trackEvent } from "@/lib/analytics";

trackEvent("booking_started", {
  serviceId,
  source: "service-page",
});

Circle Mixpanel Config

Deferred Google Tag Manager and Mixpanel initialization for Circle Health web apps.

Install

Terminal
pnpm dlx shadcn@latest add @utilities/circle-mixpanel-config

Use

TSX
import { CircleMixpanelConfig } from "@circle/ui";

Dependencies

KindPackages
Registry@utilities/analytics, @utilities/use-analytics

CircleOS PII Mask

The script masks tagged names, emails, phone numbers, birth dates, addresses, and avatars. Paste it into DevTools before screenshots or recordings. It installs window.__circlePiiMask, applies masking immediately, and watches for remounts.

Mark sensitive elements

<div data-pii="name">Jane Doe</div> <div data-pii="email">jane@example.com</div> <div data-pii="phone">+49 1512 1234567</div> <div data-pii="dob">23.09.1988 (37)</div> <div data-pii="address">Rosenthaler Str. 16\n10119 Berlin</div> <img data-pii="avatar" src="/avatar.jpg" alt="" />

Runtime API

window.__circlePiiMask.mask(); window.__circlePiiMask.unmask(); window.__circlePiiMask.toggle(); window.__circlePiiMask.destroy();

Full script

(() => { if (window.__circlePiiMask?.destroy) { window.__circlePiiMask.destroy(); } const CONFIG = { selector: "[data-pii]", avatarMode: "blur", // "blur" | "hide" }; const NAME_SEEDS = [ "Anna Schmidt", "Lukas Müller", "Sophie Fischer", "Paul Weber", "Leonie Wagner", "Jonas Becker", "Marie Hoffmann", "Felix Schäfer", "Clara Koch", "Noah Bauer", "Emma Richter", "Ben Klein", "Mila Wolf", "Leon Neumann", "Hannah Schwarz", "Finn Zimmermann", "Laura Braun", "Elias Krüger", "Johanna Hartmann", "Mats Lange", "Nele Werner", "David Schmitz", "Ida Krause", "Tom Meier", "Lina Lehmann", "Moritz Schulz", "Greta Maier", "Oskar Kraus", "Pia Keller", "Anton Herrmann", "Theresa König", "Julian Walter", "Mira Mayer", "Vincent Huber", "Paula Kaiser", "Emil Fuchs", "Amelie Peters", "Nico Lang", "Frieda Scholz", "Samuel Möller", "Lotta Weiß", "Maximilian Jung", "Carla Pohl", "Theo Simon", "Elisa Franke", "Jakob Albrecht", "Nina Vogt", "Henry Winter", "Jule Seidel", "Marlon Graf", ]; const STREET_SEEDS = [ "Lindenstrasse 12", "Bergweg 8", "Gartenallee 27", "Sonnenweg 14", "Feldstrasse 33", "Am Park 5", "Wiesenweg 19", "Birkenallee 42", "Bahnhofstrasse 11", "Mozartstrasse 24", ]; const CITY_SEEDS = [ ["10115", "Berlin"], ["20095", "Hamburg"], ["80331", "Muenchen"], ["50667", "Koeln"], ["60311", "Frankfurt am Main"], ["01067", "Dresden"], ["70173", "Stuttgart"], ["04109", "Leipzig"], ["28195", "Bremen"], ["30159", "Hannover"], ]; const textStore = new WeakMap(); const avatarStore = new WeakMap(); let observer = null; let masked = false; let applying = false; let scheduled = false; const transliterate = (value) => String(value) .normalize("NFKD") .replace(/[\u0300-\u036f]/g, "") .replace(/ä/g, "ae") .replace(/ö/g, "oe") .replace(/ü/g, "ue") .replace(/Ä/g, "Ae") .replace(/Ö/g, "Oe") .replace(/Ü/g, "Ue") .replace(/ß/g, "ss") .replace(/[^a-zA-Z0-9]+/g, "-") .replace(/^-+|-+$/g, "") .toLowerCase(); const hashString = (value) => { let hash = 2166136261; for (let index = 0; index < value.length; index += 1) { hash ^= value.charCodeAt(index); hash = Math.imul(hash, 16777619); } return hash >>> 0; }; const pickSeedName = (source) => { const index = hashString(source || "person") % NAME_SEEDS.length; return NAME_SEEDS[index]; }; const fakeEmail = (source) => { const [firstName, ...rest] = pickSeedName(source).split(" "); const lastName = rest.join(" "); const suffix = String((hashString(`email:${source}`) % 90) + 10); return `${transliterate(firstName)}.${transliterate(lastName)}${suffix}@beispiel.invalid`; }; const fakePhone = (source) => { const prefixes = [ "1512", "1523", "1550", "1577", "1590", "1602", "1704", "1718", "1726", "1763", ]; const hash = hashString(`phone:${source}`); const prefix = prefixes[hash % prefixes.length]; const tail = String(1000000 + (hash % 9000000)); return `+49 ${prefix.slice(0, 3)} ${prefix.slice(3)} ${tail.slice(0, 3)} ${tail.slice(3)}`; }; const fakeDob = (source) => { const day = String((hashString(`dob-day:${source}`) % 28) + 1).padStart( 2, "0", ); const month = String((hashString(`dob-month:${source}`) % 12) + 1).padStart( 2, "0", ); const year = String(1960 + (hashString(`dob-year:${source}`) % 40)); const age = new Date().getFullYear() - Number(year); return `${day}.${month}.${year} (${age})`; }; const fakeAddress = (source) => { const street = STREET_SEEDS[hashString(`street:${source}`) % STREET_SEEDS.length]; const [postalCode, city] = CITY_SEEDS[hashString(`city:${source}`) % CITY_SEEDS.length]; return `${street}\n${postalCode}, ${city}, DE`; }; const getText = (element) => { if ( element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement ) { return element.value; } return element.textContent || ""; }; const setText = (element, value) => { if ( element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement ) { element.value = value; return; } element.textContent = value; }; const maskTextValue = (kind, original) => { const clean = String(original || "").trim(); if (!clean) return clean; if (kind === "name") return pickSeedName(clean); if (kind === "email") return fakeEmail(clean); if (kind === "phone") return fakePhone(clean); if (kind === "dob") return fakeDob(clean); if (kind === "address") return fakeAddress(clean); return clean; }; const rememberText = (element) => { if (!textStore.has(element)) { textStore.set(element, getText(element)); } return textStore.get(element); }; const rememberAvatar = (element) => { if (!avatarStore.has(element)) { avatarStore.set(element, { filter: element.style.filter, opacity: element.style.opacity, transform: element.style.transform, transition: element.style.transition, pointerEvents: element.style.pointerEvents, }); } return avatarStore.get(element); }; const maskAvatar = (element) => { rememberAvatar(element); element.style.transition = "filter 120ms ease, opacity 120ms ease, transform 120ms ease"; element.style.pointerEvents = "none"; if (CONFIG.avatarMode === "hide") { element.style.opacity = "0.08"; element.style.filter = "grayscale(1) brightness(1.15)"; element.style.transform = "scale(1.02)"; return; } element.style.opacity = "1"; element.style.filter = "blur(12px) grayscale(1) saturate(0) brightness(1.05)"; element.style.transform = "scale(1.08)"; }; const unmaskAvatar = (element) => { const original = avatarStore.get(element); if (!original) return; element.style.filter = original.filter; element.style.opacity = original.opacity; element.style.transform = original.transform; element.style.transition = original.transition; element.style.pointerEvents = original.pointerEvents; }; const maskElement = (element) => { const kind = element.getAttribute("data-pii"); if (!kind) return; if (kind === "avatar") { maskAvatar(element); element.setAttribute("data-pii-masked", "true"); return; } const original = rememberText(element); setText(element, maskTextValue(kind, original)); if (kind === "address") { element.style.whiteSpace = "pre-line"; } element.setAttribute("data-pii-masked", "true"); }; const unmaskElement = (element) => { const kind = element.getAttribute("data-pii"); if (!kind) return; if (kind === "avatar") { unmaskAvatar(element); element.removeAttribute("data-pii-masked"); return; } if (!textStore.has(element)) return; setText(element, textStore.get(element)); if (kind === "address") { element.style.whiteSpace = ""; } element.removeAttribute("data-pii-masked"); }; const applyMask = (root = document) => { applying = true; root.querySelectorAll(CONFIG.selector).forEach(maskElement); applying = false; }; const applyUnmask = (root = document) => { applying = true; root .querySelectorAll(`${CONFIG.selector}[data-pii-masked="true"]`) .forEach(unmaskElement); applying = false; }; const scheduleRefresh = () => { if (!masked || scheduled) return; scheduled = true; requestAnimationFrame(() => { scheduled = false; applyMask(document); }); }; const startObserver = () => { if (observer) return; observer = new MutationObserver(() => { if (!applying) scheduleRefresh(); }); observer.observe(document.body, { childList: true, subtree: true, characterData: true, attributes: true, }); }; const stopObserver = () => { if (!observer) return; observer.disconnect(); observer = null; }; const api = { config: CONFIG, mask() { masked = true; applyMask(document); startObserver(); console.info("PII masking enabled"); }, unmask() { masked = false; stopObserver(); applyUnmask(document); console.info("PII masking disabled"); }, toggle() { if (masked) this.unmask(); else this.mask(); }, destroy() { this.unmask(); delete window.__circlePiiMask; }, }; window.__circlePiiMask = api; api.mask(); })();

Lab Reports

All patient data is synthetic. Regenerate the PDFs with pnpm sample-data:generate; edit scripts/sample-data/config.mjs to change the generated mix.

MVZ Labormedizin Mitte

01 · Sofia Rossi

Normal · German · A4 · Accession MVZ-2026-80109

Download PDF

02 · Robert Becker

Critical · German · A4 · Accession MVZ-2026-98064

Download PDF

10 · Lukas Müller

Normal · German · A4 · Accession MVZ-2026-68120

Download PDF

22 · Anna Schmidt

Abnormal · German · A4 · Accession MVZ-2026-40817

Download PDF

Labor Berlin Diagnostics

03 · Sofia Rossi

Normal · German · A4 · Accession BLN-2026-97142

Download PDF

08 · Lukas Müller

Abnormal · German · A4 · Accession BLN-2026-84587

Download PDF

12 · Lukas Müller

Abnormal · German · A4 · Accession BLN-2026-62015

Download PDF

Northbridge Clinical Laboratory

04 · Lukas Müller

Abnormal · English · A4 · Accession NHS-2026-40629

Download PDF

13 · Robert Becker

Abnormal · English · A4 · Accession NHS-2026-40857

Download PDF

19 · Robert Becker

Abnormal · English · A4 · Accession NHS-2026-49618

Download PDF

QuestPoint Diagnostics

05 · Anna Schmidt

Abnormal · English · US Letter · Accession QST-2026-89814

Download PDF

07 · Lukas Müller

Normal · English · US Letter · Accession QST-2026-39665

Download PDF

15 · James Okafor

Abnormal · English · US Letter · Accession QST-2026-29730

Download PDF

17 · Fatima El-Amin

Normal · English · US Letter · Accession QST-2026-35196

Download PDF

21 · Robert Becker

Abnormal · English · US Letter · Accession QST-2026-29430

Download PDF

Synevo Labs

06 · Noah Andersson

Critical · English · A4 · Accession SYN-2026-19468

Download PDF

09 · Noah Andersson

Abnormal · English · A4 · Accession SYN-2026-37784

Download PDF

11 · Sofia Rossi

Abnormal · English · A4 · Accession SYN-2026-60192

Download PDF

14 · Mei Lin Tan

Critical · English · A4 · Accession SYN-2026-52760

Download PDF

20 · Fatima El-Amin

Abnormal · English · A4 · Accession SYN-2026-76246

Download PDF

23 · Fatima El-Amin

Abnormal · English · A4 · Accession SYN-2026-63482

Download PDF

Amedes Genetics & Lab

16 · James Okafor

Normal · German · A4 · Accession AMD-2026-38168

Download PDF

18 · Mei Lin Tan

Abnormal · German · A4 · Accession AMD-2026-15382

Download PDF

24 · James Okafor

Normal · German · A4 · Accession AMD-2026-15674

Download PDF

Intake Forms

All patient data is synthetic. Regenerate the PDFs with pnpm sample-data:generate.

Praxis Kessler — Initial Naturopathy Intake

Anna Schmidt

Ready · German · Naturopathy Consultation

Download PDF

Blank template

Blank · German · Naturopathy Consultation

Download PDF

Decades — Preventive Diagnostics Intake

Robert Becker

Signed · English · Full Body Diagnostic

Download PDF

Blank template

Blank · English · Full Body Diagnostic

Download PDF

Soma Studio — Movement and Rehab Intake

Sofia Rossi

Ready · English · Movement Analysis

Download PDF

Blank template

Blank · English · Movement Analysis

Download PDF

Brands overview

Illustrative customer brands for Circle Health product demos and marketing. Each profile combines a visual identity, service model, staff, and representative circleOS configuration.

API

The brands endpoint exposes the same synthetic profiles.

Method and pathReturns
GET /api/brands{ count, brands } for all demo brands.
GET /api/brands?id=<id>One brand, or 404 when the id is unknown.

Valid ids are praxis-kessler, decades, and soma-studio.

const { brands } = await fetch( "https://registry.circle.health/api/brands", ).then((response) => response.json()); const decades = await fetch( "https://registry.circle.health/api/brands?id=decades", ).then((response) => response.json());

Brand shape

type Brand = { id: string; name: string; type: string; shortDescription: string; description: string; website: string; offering: string; logo: string; initials: string; typeface: string; palette: { bg: string; primary: string; accent: string; ink: string; onPrimary: string; }; locations: string[]; services: Array<{ name: string; description: string; icon: string; image: string; }>; staff: Array<{ name: string; role: string; image: string; }>; personality: string; circleOS: { used: string[]; notUsed: string[]; }; };

Image fields are absolute URLs against the request origin.

Sample-data manifest

GET /sample-data/manifest.json returns the generated PDF inventory used by the lab-report and intake-form galleries.

Praxis Kessler

Overview

Praxis Kessler logo

Small solo Heilpraktiker

Naturopathy, acupuncture & herbal medicine

A one-woman naturopathic practice in Hamburg, built on long-term patient relationships.

Profile

About

A one-woman naturopathic practice run by Nora Kessler in Hamburg-Eimsbüttel, opened in 2017. Nora sees 12 to 15 patients per week, mostly long-term regulars seeking complements to conventional medicine. She is methodical, warm, and skeptical of tech but uses circleOS because it helps her manage every part of her business in one place.

Personality

Traditional and relationship-first. She knows most of her patients by name and has been treating some for years. She is not interested in growth for its own sake — what matters is doing good work and running a calm, orderly practice.

Locations

Hamburg-Eimsbüttel

Brand identity

Asset
/brands/logos/kessler.png
Fallback initials
NK

Typography

Jost / Inter

Website

praxis-kessler.de

Colour palette

Primary
#9C8B7E
#9C8B7E
Accent
#6F5F52
#6F5F52
Ink
#25201C
#25201C
Surface
#F5F0EB
#F5F0EB
On primary
#FFFFFF
#FFFFFF

Team

Services

circleOS configuration

In use

Not used

API

Fetch this brand

praxis-kessler.ts
const response = await fetch(
  "https://registry.circle.health/api/brands?id=praxis-kessler",
);

if (!response.ok) {
  throw new Error(`Brand request failed: ${response.status}`);
}

const brand = await response.json();

Complete profile

The complete profile below includes every value shown on this page. Copy it as JSON for fixtures, prompts, or prototypes.

praxis-kessler.json
{
  "id": "praxis-kessler",
  "name": "Praxis Kessler",
  "type": "Small solo Heilpraktiker",
  "shortDescription": "A one-woman naturopathic practice in Hamburg, built on long-term patient relationships.",
  "description": "A one-woman naturopathic practice run by Nora Kessler in Hamburg-Eimsbüttel, opened in 2017. Nora sees 12 to 15 patients per week, mostly long-term regulars seeking complements to conventional medicine. She is methodical, warm, and skeptical of tech but uses circleOS because it helps her manage every part of her business in one place.",
  "website": "praxis-kessler.de",
  "offering": "Naturopathy, acupuncture & herbal medicine",
  "logo": "/brands/logos/kessler.png",
  "initials": "NK",
  "typeface": "Jost / Inter",
  "palette": {
    "bg": "#F5F0EB",
    "primary": "#9C8B7E",
    "accent": "#6F5F52",
    "ink": "#25201C",
    "onPrimary": "#FFFFFF"
  },
  "locations": [
    "Hamburg-Eimsbüttel"
  ],
  "services": [
    {
      "name": "Naturopathy Consultation",
      "description": "90 min initial deep-dive, 45 min follow-ups.",
      "icon": "consultation",
      "image": "/brands/kessler/services/naturopathy.png"
    },
    {
      "name": "Acupuncture",
      "description": "Pain, stress, and hormonal balance.",
      "icon": "needle",
      "image": "/brands/kessler/services/acupuncture.png"
    },
    {
      "name": "Manual Therapy",
      "description": "Lymphatic drainage and gentle bodywork.",
      "icon": "hands",
      "image": "/brands/kessler/services/manual-therapy.png"
    },
    {
      "name": "Nutritional Counselling",
      "description": "Gut health, elimination diets, and metabolic balance.",
      "icon": "nutrition",
      "image": "/brands/kessler/services/nutrition.png"
    },
    {
      "name": "Phytotherapy",
      "description": "Plant-based prescriptions and herbal protocols.",
      "icon": "herb",
      "image": "/brands/kessler/services/phytotherapy.png"
    }
  ],
  "staff": [
    {
      "name": "Nora Kessler",
      "role": "Heilpraktikerin & Founder",
      "image": "/brands/kessler/staff/nora-kessler.png"
    },
    {
      "name": "Practice Admin",
      "role": "Part-time administrator",
      "image": "/brands/kessler/staff/admin.png"
    }
  ],
  "personality": "Traditional and relationship-first. She knows most of her patients by name and has been treating some for years. She is not interested in growth for its own sake — what matters is doing good work and running a calm, orderly practice.",
  "circleOS": {
    "used": [
      "Online booking for new and returning patients",
      "Intake forms sent via SMS before first appointments",
      "Appointment reminders by email and SMS",
      "Invoicing and billing for private patients",
      "Patient notes and long-term tracking per patient",
      "Plans for recurring treatment programs"
    ],
    "notUsed": [
      "Multi-location configuration (single location only)",
      "Staff roles and permissions (solo practitioner)",
      "Lab order management (no diagnostic services)"
    ]
  }
}

Decades

Overview

Decades logo

Multi-city premium preventive health company

Whole-body diagnostics & longevity programs

Preventive medicine clinics catching serious illness years before it becomes a problem.

Profile

About

Founded in Berlin in 2019, Decades is a preventive medicine clinic built on the belief that most serious illness is detectable years before it becomes a problem. Named for the decade of life you can add by catching things early, the company offers comprehensive diagnostic programs combining imaging, lab analysis, and physician consultations. The brand is precise, authoritative, and understated.

Personality

Clinical but not cold. Decades speaks to people who treat their health like an investment. The tone is direct, evidence-based, and confident. No wellness language, no motivational copy.

Locations

Brand identity

Asset
/brands/logos/decades.png
Fallback initials
D

Typography

Tenor Sans / Inter

Website

decades.health

Colour palette

Primary
#1C453E
#1C453E
Accent
#5A7870
#5A7870
Ink
#1C2B27
#1C2B27
Surface
#F1F4F2
#F1F4F2
On primary
#FFFFFF
#FFFFFF

Team

Services

circleOS configuration

In use

Not used

API

Fetch this brand

decades.ts
const response = await fetch(
  "https://registry.circle.health/api/brands?id=decades",
);

if (!response.ok) {
  throw new Error(`Brand request failed: ${response.status}`);
}

const brand = await response.json();

Complete profile

The complete profile below includes every value shown on this page. Copy it as JSON for fixtures, prompts, or prototypes.

decades.json
{
  "id": "decades",
  "name": "Decades",
  "type": "Multi-city premium preventive health company",
  "shortDescription": "Preventive medicine clinics catching serious illness years before it becomes a problem.",
  "description": "Founded in Berlin in 2019, Decades is a preventive medicine clinic built on the belief that most serious illness is detectable years before it becomes a problem. Named for the decade of life you can add by catching things early, the company offers comprehensive diagnostic programs combining imaging, lab analysis, and physician consultations. The brand is precise, authoritative, and understated.",
  "website": "decades.health",
  "offering": "Whole-body diagnostics & longevity programs",
  "logo": "/brands/logos/decades.png",
  "initials": "D",
  "typeface": "Tenor Sans / Inter",
  "palette": {
    "bg": "#F1F4F2",
    "primary": "#1C453E",
    "accent": "#5A7870",
    "ink": "#1C2B27",
    "onPrimary": "#FFFFFF"
  },
  "locations": [
    "Berlin (HQ)",
    "Hamburg",
    "Munich",
    "Frankfurt",
    "Cologne"
  ],
  "services": [
    {
      "name": "Full Body Diagnostic",
      "description": "Whole-body MRI, blood panel, and physician debrief in one day.",
      "icon": "scan",
      "image": "/brands/decades/services/full-body.png"
    },
    {
      "name": "Cardiovascular Screening",
      "description": "ECG, echocardiography, arterial stiffness, and lipid analysis.",
      "icon": "heart",
      "image": "/brands/decades/services/cardiovascular.png"
    },
    {
      "name": "Cancer Early Detection",
      "description": "Multi-cancer blood test combined with targeted imaging.",
      "icon": "dna",
      "image": "/brands/decades/services/cancer.png"
    },
    {
      "name": "Metabolic & Hormonal Analysis",
      "description": "Comprehensive lab workup with dietary and lifestyle follow-up.",
      "icon": "lab",
      "image": "/brands/decades/services/metabolic.png"
    },
    {
      "name": "Annual Membership Program",
      "description": "Yearly reassessment, result tracking, and priority scheduling.",
      "icon": "membership",
      "image": "/brands/decades/services/membership.png"
    }
  ],
  "staff": [
    {
      "name": "Dr. Jonas Weiß",
      "role": "Medical Director",
      "image": "/brands/decades/staff/jonas-weiss.png"
    },
    {
      "name": "Dr. Lena Brandt",
      "role": "Lead Radiologist",
      "image": "/brands/decades/staff/lena-brandt.png"
    },
    {
      "name": "Location Medical Leads",
      "role": "One per city (5)"
    },
    {
      "name": "Coordination Teams",
      "role": "Per-location admin & ops"
    }
  ],
  "personality": "Clinical but not cold. Decades speaks to people who treat their health like an investment. The tone is direct, evidence-based, and confident. No wellness language, no motivational copy.",
  "circleOS": {
    "used": [
      "Multi-location scheduling across 5 cities with capacity management",
      "Patient profiles with full diagnostic history and longitudinal tracking",
      "Lab order management and result documentation",
      "Standardized intake and consent forms before each appointment",
      "AI briefings before physician consultations",
      "Patient portal for results, follow-ups, and secure messaging",
      "Invoicing for private-pay diagnostic packages",
      "Staff roles and permissions per location",
      "Reporting analytics across locations"
    ],
    "notUsed": [
      "Single-practitioner workflows (operates as a team)",
      "SMS-based intake (all forms go through the patient portal)",
      "Supplement prescriptions"
    ]
  }
}

Soma Studio

Overview

Soma Studio logo

Boutique integrative health studio

Osteopathy, sports medicine & rehab

A boutique Munich studio treating physical health as structural, functional, and personal.

Profile

About

Co-founded in Munich in 2022 by osteopath Clara Reuter and sports physician Dr. Felix Naumann, Soma Studio is a small practice built around the idea that physical health is structural, functional, and personal. One location in Munich-Schwabing with a growing online program. The studio works with a mix of athletes, desk workers, and people recovering from chronic pain or injury. It is intentionally small.

Personality

Grounded, precise, and quietly confident. Soma does not market aggressively — its reputation comes from referrals and a loyal patient base. The tone is knowledgeable without being clinical, warm without being soft.

Locations

Brand identity

Asset
/brands/logos/soma.png
Fallback initials
S

Typography

Poppins / Inter

Website

soma-studio.de

Colour palette

Primary
#0A0A0A
#0A0A0A
Accent
#3B6CB5
#3B6CB5
Ink
#1C1C1E
#1C1C1E
Surface
#F3F0EC
#F3F0EC
On primary
#FFFFFF
#FFFFFF

Team

Services

circleOS configuration

In use

Not used

API

Fetch this brand

soma-studio.ts
const response = await fetch(
  "https://registry.circle.health/api/brands?id=soma-studio",
);

if (!response.ok) {
  throw new Error(`Brand request failed: ${response.status}`);
}

const brand = await response.json();

Complete profile

The complete profile below includes every value shown on this page. Copy it as JSON for fixtures, prompts, or prototypes.

soma-studio.json
{
  "id": "soma-studio",
  "name": "Soma Studio",
  "type": "Boutique integrative health studio",
  "shortDescription": "A boutique Munich studio treating physical health as structural, functional, and personal.",
  "description": "Co-founded in Munich in 2022 by osteopath Clara Reuter and sports physician Dr. Felix Naumann, Soma Studio is a small practice built around the idea that physical health is structural, functional, and personal. One location in Munich-Schwabing with a growing online program. The studio works with a mix of athletes, desk workers, and people recovering from chronic pain or injury. It is intentionally small.",
  "website": "soma-studio.de",
  "offering": "Osteopathy, sports medicine & rehab",
  "logo": "/brands/logos/soma.png",
  "initials": "S",
  "typeface": "Poppins / Inter",
  "palette": {
    "bg": "#F3F0EC",
    "primary": "#0A0A0A",
    "accent": "#3B6CB5",
    "ink": "#1C1C1E",
    "onPrimary": "#FFFFFF"
  },
  "locations": [
    "Munich-Schwabing",
    "Online (Germany-wide video)"
  ],
  "services": [
    {
      "name": "Osteopathic Treatment",
      "description": "Structural assessment and hands-on treatment for pain, posture, and mobility.",
      "icon": "hands",
      "image": "/brands/soma/services/osteopathy.png"
    },
    {
      "name": "Sports Medicine Consultation",
      "description": "Injury assessment, return-to-sport planning, and performance health.",
      "icon": "consultation",
      "image": "/brands/soma/services/sports-medicine.png"
    },
    {
      "name": "Movement Analysis",
      "description": "Video-based gait and movement screening with exercise prescription.",
      "icon": "movement",
      "image": "/brands/soma/services/movement.png"
    },
    {
      "name": "Online Physiotherapy",
      "description": "Guided video sessions with associate physiotherapists.",
      "icon": "video",
      "image": "/brands/soma/services/physiotherapy.png"
    },
    {
      "name": "Quarterly Body MOT",
      "description": "Combined osteopathy and sports medicine check-in for active patients.",
      "icon": "rehab"
    }
  ],
  "staff": [
    {
      "name": "Clara Reuter",
      "role": "Osteopath & Co-founder",
      "image": "/brands/soma/staff/clara-reuter.png"
    },
    {
      "name": "Dr. Felix Naumann",
      "role": "Sports Physician & Co-founder",
      "image": "/brands/soma/staff/felix-naumann.png"
    },
    {
      "name": "Associate Physiotherapists",
      "role": "3 therapists"
    },
    {
      "name": "Studio Coordinator",
      "role": "Front-of-house & scheduling",
      "image": "/brands/soma/staff/coordinator.png"
    }
  ],
  "personality": "Grounded, precise, and quietly confident. Soma does not market aggressively — its reputation comes from referrals and a loyal patient base. The tone is knowledgeable without being clinical, warm without being soft.",
  "circleOS": {
    "used": [
      "Online booking for in-person and video sessions",
      "Therapist profiles with speciality and availability",
      "Intake and consent forms before first appointments",
      "Notes and session documentation per patient",
      "Movement analysis files attached to patient profiles",
      "Appointment reminders by email and SMS",
      "Plans for phased rehabilitation programs",
      "Patient portal for notes, resources, and follow-up booking",
      "Invoicing for self-pay and private insurance patients"
    ],
    "notUsed": [
      "Multi-location scheduling (one physical location)",
      "Lab order management (no in-house diagnostics)",
      "Staff permissions across teams (small flat team)"
    ]
  }
}