# Horizon globe
> The top of a large dotted globe rising from the bottom edge, for page heroes, with arcs that climb into the sky between places.
- React: `import { GeoHorizon } from "@/components/lumesec/geo-horizon"`
- Collection: Location (https://elements.lumesec.ai/components/location)
- Data: takes your data (`data`); shows demo data until you pass it
- Registry item: https://elements.lumesec.ai/r/geo-horizon.json
- Page: https://elements.lumesec.ai/components/location/horizon



Live preview: https://elements.lumesec.ai/view/geo-horizon

Demo source:

```tsx
"use client";

import * as React from "react";

import { GeoHorizon } from "@/components/lumesec/geo-horizon";

export default function GeoHorizonDemo() {
  const titleId = React.useId();
  return (
    <GeoHorizon aria-labelledby={titleId} capHeight={0.4} className="h-[420px] w-full rounded-[14px] border border-border">
      <p className="m-0 flex items-center gap-2 font-mono text-[11px] leading-4 tracking-[0.14em] text-muted-foreground uppercase">
        <span aria-hidden="true" className="size-1.5 rounded-[1px] bg-lumesec" />
        Global network
      </p>
      <h2
        id={titleId}
        className="m-0 max-w-[680px] text-[23px] leading-[1.15] font-semibold tracking-[-0.022em] text-balance text-foreground sm:text-[29px]"
      >
        Requests between 12 regions, traced end to end
      </h2>
      <p className="m-0 max-w-[520px] text-[14px] leading-[1.5] text-pretty text-muted-foreground">
        From the edge that receives a request to the region that answers it.
      </p>
      <div className="mt-1 flex flex-wrap justify-center gap-2">
        <button
          type="button"
          className="h-[34px] cursor-pointer rounded-lg border border-foreground bg-foreground px-3.5 font-[inherit] text-[13px] font-medium text-background outline-none hover:opacity-90 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-solid focus-visible:outline-lumesec"
        >
          View regions
        </button>
        <button
          type="button"
          className="h-[34px] cursor-pointer rounded-lg border border-border bg-card px-3.5 font-[inherit] text-[13px] font-medium text-foreground outline-none hover:border-[color-mix(in_srgb,var(--lumesec)_50%,var(--border))] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-solid focus-visible:outline-lumesec"
        >
          Read the docs
        </button>
      </div>
    </GeoHorizon>
  );
}
```

> **Your data:** Pass your pairs of places as `connections`. Without it the component shows arcs between demo cities, and between demo land points where no two cities are on the visible cap. See [Your data](/docs/data).

## Playground

Change a prop and the component re-renders. Props marked remounts set an initial value, so the component starts over.

## Installation

```bash
npx shadcn@latest add @lumesec/geo-horizon
```

First time with the @lumesec registry? Register it once, or install by URL:

```bash
npx shadcn@latest registry add @lumesec=https://elements.lumesec.ai/r/{name}.json
npx shadcn@latest add https://elements.lumesec.ai/r/geo-horizon.json
```

## Usage

React:

```tsx
import { GeoHorizon, type HorizonConnection } from "@/components/lumesec/geo-horizon";

const connections: HorizonConnection[] = [
  { from: { lat: 40.71, lon: -74.01 }, to: { lat: 51.51, lon: -0.13 } },
  { from: { lat: 38.72, lon: -9.14 }, to: { lat: 43.65, lon: -79.38 } },
  { from: { lat: 41.88, lon: -87.63 }, to: { lat: 50.11, lon: 8.68 } },
];

export function Hero() {
  return (
    <GeoHorizon aria-labelledby="hero-title" connections={connections} className="h-[480px]">
      <h1 id="hero-title" className="text-3xl font-semibold tracking-tight">
        Requests between regions
      </h1>
      <p className="text-muted-foreground">From the edge that receives a request to the region that answers it.</p>
    </GeoHorizon>
  );
}
```

## Behaviour

The top of a large dotted globe rising from the bottom edge, for page heroes. On first view the horizon line draws out from its middle and the land appears behind an uneven, bright front that spreads from the same point.

Then connections leave the surface, climb into the sky above the horizon and come down in another region, where a ring of dots lights up, while a glint runs along the limb now and then. Your headline and actions are regular children over the sky. The globe drifts slowly while it is in view.

## API reference

### Props

Also accepts every prop of `<section>` (`React.ComponentProps<"section">`), spread onto the root element.

| Prop          | Type                           | Default                 | Description                                                                                                                                                                                                                                                                |
| ------------- | ------------------------------ | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `children`    | `React.ReactNode`              | —                       | Headline, copy and actions. They sit over the sky in a column above the globe and stay fully interactive.                                                                                                                                                                  |
| `connections` | `readonly HorizonConnection[]` | —                       | Pairs of places to arc between: `{ from: LatLon; to: LatLon }`. Pairs play in order, one every `arcInterval` seconds, when both ends are on the visible cap; the others wait until the drift brings them round. An empty array draws no arcs. Without it, demo pairs play. |
| `center`      | `LatLon`                       | `{ lat: 38, lon: -40 }` | The point shown in the lower third of the visible cap. The default shows the North Atlantic with both coasts. A new value springs into place.                                                                                                                              |
| `capHeight`   | `number`                       | `0.46`                  | Share of the root's height the cap takes, 0.25 to 0.7.                                                                                                                                                                                                                     |
| `align`       | `"center" \| "start"`          | `"center"`              | Alignment of the children: a centred column, or left-aligned with a wider inset.                                                                                                                                                                                           |
| `atmosphere`  | `boolean`                      | `true`                  | Draws the accent glow above the limb. The 1px limb line stays either way.                                                                                                                                                                                                  |
| `drift`       | `boolean`                      | `true`                  | Turns the globe slowly from west to east, like the Earth, while it is on screen.                                                                                                                                                                                           |
| `driftSpeed`  | `number`                       | `1.2`                   | Drift in degrees of longitude per second.                                                                                                                                                                                                                                  |
| `reveal`      | `"view" \| "mount" \| "none"`  | `"view"`                | When the land reveal plays: once about a third of the component is on screen, right after mount, or never (the land is there from the start).                                                                                                                              |
| `arcInterval` | `number`                       | `1.4`                   | Seconds between new arcs. At most four arcs are in flight; each grows for 1.6 s, holds for 0.6 s and erases toward its destination over 0.9 s.                                                                                                                             |

### Ref

`ref` points at the root `HTMLElement`.

## Accessibility

* The root is a `section` and takes every `section` prop. Name it with `aria-labelledby` pointing at the headline in `children`.
* The canvas is `aria-hidden` and ignores the pointer. The globe, the arcs and the glint are decoration; nothing a reader needs lives only in the canvas.
* Children render above the canvas in document order and keep their own focus, keyboard and pointer handling.
* The top 38% of the canvas is masked out, so text over the sky sits on the plain background.
* Reduced motion: the land shows without the reveal, the globe does not drift, the glint along the limb is off, and up to three connections are drawn as still dotted arcs that change every 4 seconds, without comets or landing rings.

## Theming

Styled with Tailwind classes on your shadcn theme tokens, so light and dark follow your theme. The accent comes from `--lumesec`. See [Theming](/docs/theming).

This component reads `--background`, `--foreground`, `--card`, `--lumesec`, `--lumesec-glint` and `--lumesec-shine`.

## Notes

* Give the root its size, for example `h-[420px]` or a parent with a fixed height. It fills `h-full` with a minimum of 340px. The globe's radius is the larger of 0.95 × width and 1.25 × height, so narrow screens show a flatter, narrower cap.
* On first view the horizon line draws out from its middle, then the land appears behind an uneven, bright front that spreads from the same point. The reveal plays once per mount.
* Each arc leaves its source with a small ring of light, travels as a dotted line with a comet at its head and lands with a wider ring that flattens into an ellipse near the horizon. Arcs between distant places rise higher into the sky.
* The animation runs only while the component is on screen and continues where it left off when it returns. With `drift` off, the loop also sleeps between arcs.
* The globe is drawn from the embedded world data as land dots. There are no map tiles and no network requests.


