# Domain Switch: Focus Mode

> A writing app. Focus opens a calm, dark domain in which the sidebar and toolbar, wrapped in `DomainSwitchAside`, dissolve, and the document in `DomainSwitchMain` comes forward. One `Editor` renders both worlds. Esc leaves.

A shadcn/ui block from the [@keyframeui](https://keyframeui.com/?utm_source=shadcn.exchange) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/keyframeui/domain-switch-focus-demo).

- Page, with a live preview: https://shadcn.exchange/r/keyframeui/domain-switch-focus-demo
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/keyframeui/domain-switch-focus-demo-light-835320c927461408.png
- Registry item: https://keyframeui.com/r/domain-switch-focus-demo.json
- Categories: toggle, theme, transition, canvas
- Listed: 2026-10-23, source last changed 2026-10-23

## Install

```bash
npx shadcn@latest add https://shadcn.exchange/r/keyframeui/domain-switch-focus-demo.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- Registry items: `https://keyframeui.com/r/domain-switch.json`

## Notes from the registry

Live preview, examples and props: https://keyframeui.com/components/domain-switch#domain-switch-focus-demo

## Files

### `registry/demos/domain-switch-focus-demo.tsx`

```tsx
"use client";

import * as React from "react";

import { cn } from "@/lib/utils";
import {
  DomainSwitch,
  DomainSwitchAside,
  DomainSwitchMain,
  DomainSwitchScope,
  DomainSwitchToggle,
  useDomainSwitch,
} from "@/components/ui/domain-switch";

const drafts = ["On slow software", "Field notes, March", "The case for less", "Interview: K. Ito"];

export function DomainSwitchFocusDemo() {
  const [focus, setFocus] = React.useState(false);

  React.useEffect(() => {
    if (!focus) return;
    const onKeyDown = (event: KeyboardEvent) => {
      if (event.key === "Escape") setFocus(false);
    };
    window.addEventListener("keydown", onKeyDown);
    return () => window.removeEventListener("keydown", onKeyDown);
  }, [focus]);

  return (
    <div className="w-full overflow-hidden rounded-xl border border-zinc-200 dark:border-zinc-800">
      <DomainSwitch checked={focus} onCheckedChange={setFocus}>
        <DomainSwitchScope className="h-[var(--demo-height,560px)]" seamClassName="text-stone-950">
          <Editor />
        </DomainSwitchScope>
      </DomainSwitch>
    </div>
  );
}

// One editor for both worlds; it reads which one it's in.
function Editor() {
  const { world } = useDomainSwitch();
  const calm = world === "inside";

  return (
    <div
      className={cn(
        "flex h-full",
        calm ? "bg-[#141311] text-stone-300" : "bg-white text-stone-800",
      )}
    >
      <DomainSwitchAside
        className={cn(
          "hidden w-52 shrink-0 flex-col gap-1 border-r p-3 sm:flex",
          calm ? "border-white/5" : "border-stone-200 bg-stone-50",
        )}
      >
        <p className="px-2 pt-1 pb-2 text-xs font-medium text-stone-400">Drafts</p>
        {drafts.map((draft, i) => (
          <span
            key={draft}
            className={cn(
              "truncate rounded-md px-2 py-1.5 text-sm",
              i === 0 ? (calm ? "bg-white/5 text-stone-200" : "bg-white text-stone-900 shadow-xs") : "text-stone-500",
            )}
          >
            {draft}
          </span>
        ))}
      </DomainSwitchAside>

      <div className="flex min-w-0 flex-1 flex-col">
        <div
          className={cn(
            "flex items-center gap-1 border-b px-3 py-2",
            calm ? "border-transparent" : "border-stone-200",
          )}
        >
          <DomainSwitchAside className="flex items-center gap-1">
            {["B", "I", "U"].map((mark) => (
              <span
                key={mark}
                className={cn(
                  "grid size-8 place-items-center rounded-md text-sm text-stone-500",
                  mark === "B" && "font-bold",
                  mark === "I" && "italic",
                  mark === "U" && "underline",
                )}
              >
                {mark}
              </span>
            ))}
            <span className="mx-1 h-4 w-px bg-stone-200" />
            <span className="grid h-8 place-items-center rounded-md px-2 text-sm text-stone-500">H1</span>
            <span className="hidden h-8 place-items-center rounded-md px-2 text-sm text-stone-500 sm:grid">Quote</span>
          </DomainSwitchAside>
          {calm && <span className="ml-1 text-xs text-stone-500">Esc to leave</span>}
          <DomainSwitchToggle
            className={cn(
              "ml-auto h-8 gap-1.5 rounded-full px-3 text-xs font-medium",
              calm
                ? "bg-amber-200/10 text-amber-100 hover:bg-amber-200/15"
                : "bg-stone-900 text-white hover:bg-stone-800",
            )}
          >
            <FocusIcon />
            {calm ? "Leave focus" : "Focus"}
          </DomainSwitchToggle>
        </div>

        <div className="min-h-0 flex-1 overflow-hidden px-6 pt-8 sm:px-10 sm:pt-12">
          <DomainSwitchMain className="mx-auto max-w-[34rem] origin-top">
            <p className={cn("text-xs", calm ? "text-stone-500" : "text-stone-400")}>Essay · 412 words</p>
            <h2
              className={cn(
                "mt-2 font-serif text-3xl tracking-tight sm:text-4xl",
                calm ? "text-stone-100" : "text-stone-900",
              )}
            >
              On slow software
            </h2>
            <div className="mt-5 space-y-4 font-serif text-[15px]/7 sm:text-base/7">
              <p>
                The best tools I use don&apos;t ask for my attention. They wait. A text editor that opens before
                I&apos;ve finished the thought, a calendar that doesn&apos;t blink.
              </p>
              <p>
                Most software is built to be noticed. Badges, banners, a tour on every release. Each one is
                small, and together they are the reason I can&apos;t finish a paragraph
                <span
                  className={cn(
                    "ml-0.5 inline-block h-[1.1em] w-px translate-y-[3px] animate-pulse",
                    calm ? "bg-amber-200" : "bg-stone-900",
                  )}
                />
              </p>
            </div>
          </DomainSwitchMain>
        </div>
      </div>
    </div>
  );
}

function FocusIcon() {
  return (
    <svg viewBox="0 0 16 16" className="size-3.5" fill="none" stroke="currentColor" strokeWidth="1.5" aria-hidden>
      <circle cx="8" cy="8" r="2.25" />
      <path d="M2 5V3a1 1 0 0 1 1-1h2M11 2h2a1 1 0 0 1 1 1v2M14 11v2a1 1 0 0 1-1 1h-2M5 14H3a1 1 0 0 1-1-1v-2" strokeLinecap="round" />
    </svg>
  );
}
```
