first commit
This commit is contained in:
87
docs/ui/react/react-ui/src/components/spinner/spinner.md
Normal file
87
docs/ui/react/react-ui/src/components/spinner/spinner.md
Normal file
@@ -0,0 +1,87 @@
|
||||
# Spinner
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs --> - spinner.variants — `spinnerVariants` is the cva con…
|
||||
- [spinner.variants](./spinner.variants.md) — `spinnerVariants` is the cva config that powers the `<Spinner>` chassis component. It exposes a `size` axis (`sm` / `de…
|
||||
- spinner.variants — `spinnerVariants` is the cva con…
|
||||
|
||||
- [spinner.variants](./spinner.variants.md) — `spinnerVariants` is the cva config that powers the `<Spinner>` chassis component. It exposes a `size` axis (`sm` / `de…
|
||||
|
||||
- spinner.variants — `spinnerVariants` is the cva con…
|
||||
|
||||
- [spinner.variants](./spinner.variants.md) — `spinnerVariants` is the cva config that powers the `<Spinner>` chassis component. It exposes a `size` axis (`sm` / `de…
|
||||
|
||||
- spinner.variants — `spinnerVariants` is the cva con…
|
||||
|
||||
- [spinner.variants](./spinner.variants.md) — `spinnerVariants` is the cva config that powers the `<Spinner>` chassis component. It exposes a `size` axis (`sm` / `de…
|
||||
- [spinner.variants](./spinner.variants.md) — `spinnerVariants` is the cva config that powers the `<Spinner>` chassis component. It exposes a `size` axis (`sm` / `de…
|
||||
|
||||
## Purpose
|
||||
|
||||
`<Spinner>` is the chassis circular loading indicator for `@suaveplan/react-ui`. It renders a `<div>` with one transparent border edge that rotates via Tailwind's `animate-spin`, creating the familiar "spinning arc" pattern. The animation is gated behind `motion-safe:animate-spin` so users with `prefers-reduced-motion: reduce` see a static circle while everyone else sees the rotation.
|
||||
|
||||
The component mirrors the upstream shadcn `spinner` registry item (per Wave 3a's reconciliation finding #6: shadcn ships `spinner` as a discrete registry item and the chassis mirrors that surface). Per the shadcn skill rule, the recommended composition for a button loading state is `<Spinner>` + `data-icon` + `disabled` — the `Button` itself does NOT carry an `isLoading` prop because hardcoding the loading affordance into the Button forecloses every other composition consumers might want (label-only spinner, spinner-with-progress, replace-trigger spinner, etc.).
|
||||
|
||||
## Features
|
||||
|
||||
- Three sizes via the `size` axis (`sm` / `default` / `lg`) using Tailwind `size-*`.
|
||||
- `motion-safe:animate-spin` + `motion-reduce:animate-none` so reduced-motion users see a static circle.
|
||||
- `role="status"` automatically applied so screen-readers announce the loading state.
|
||||
- `data-spinner="true"` attribute on the rendered element so tests / consumer CSS can target every spinner via a single selector.
|
||||
- Border colour inherits via `border-current` — consumers recolour with any `text-*` utility.
|
||||
- Forwards refs to the underlying `<div>`.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import { Spinner } from "@suaveplan/react-ui";
|
||||
|
||||
export function LoadingIndicator() {
|
||||
return <Spinner />;
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import { Button, Spinner } from "@suaveplan/react-ui";
|
||||
|
||||
export function SaveButton({ isSaving }: { isSaving: boolean }) {
|
||||
return (
|
||||
<Button disabled={isSaving}>
|
||||
{isSaving ? <Spinner data-icon="inline-start" size="sm" /> : null}
|
||||
{isSaving ? "Saving…" : "Save changes"}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `<Spinner>`
|
||||
|
||||
`forwardRef<HTMLDivElement, SpinnerProps>` — renders a `<div>` with the variant classes + consumer-supplied `className` merged via `cn()`. `role="status"` and `data-spinner="true"` are always applied.
|
||||
|
||||
#### `SpinnerProps`
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `size` | `"sm" \| "default" \| "lg"` | `"default"` | Diameter — uses Tailwind `size-*` to set width and height together. |
|
||||
| _all_ `HTMLAttributes<HTMLDivElement>` | _native_ | _native_ | Forwarded as-is — `aria-label`, `data-icon`, `id`, `style`, etc. |
|
||||
|
||||
#### `spinnerVariants`
|
||||
|
||||
The bare cva resolver — exported so external consumers (`@suaveplan/react-ui-theme-*`, ejected source) can re-use the variants directly.
|
||||
|
||||
#### Errors
|
||||
|
||||
The Spinner component does NOT call `useTranslation()` so it does not interact with the i18n provider at all. It is safe to render outside a `<TranslationProvider>` / `<ReactUiProvider>`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- `motion-safe:animate-spin` and `motion-reduce:animate-none` are baked into the cva base class string rather than driven from `motionTokens.respectReducedMotion` — Tailwind's `motion-*` variants honour `prefers-reduced-motion` natively at the CSS layer, so no JS reactivity is required and the spinner stays a leaf component.
|
||||
- `role="status"` is unconditionally applied so screen-readers announce "loading" without requiring the consumer to remember to wrap in a live region. When the spinner sits inside a Button that already has its own accessible name (e.g. "Save changes"), the consumer can suppress the duplicate announcement by passing `aria-hidden="true"`.
|
||||
- The chassis intentionally does NOT inject default `aria-label="Loading"` text — that would force every consumer to pay the i18n cost for a spinner that is often purely decorative. Consumers that need a translated label compose with `t('react-ui.progress.fallback')` or any other `TranslationKey`.
|
||||
- The `data-spinner="true"` selector is preserved across all variants; `[data-spinner="true"]` works as a global CSS hook.
|
||||
- Border thickness scales with size (`border-2` for `sm`/`default`, `border-[3px]` for `lg`) so the spinner's arc-to-diameter ratio remains visually consistent at every size.
|
||||
@@ -0,0 +1,50 @@
|
||||
# Spinner Variants
|
||||
|
||||
## Purpose
|
||||
|
||||
`spinnerVariants` is the cva config that powers the `<Spinner>` chassis component. It exposes a `size` axis (`sm` / `default` / `lg`) using Tailwind's `size-*` shorthand so the rendered diameter changes in lockstep with width and height. The base classes establish the circular shape, the transparent top-edge that creates the "spinning arc" illusion, and the `motion-safe:animate-spin` gating that disables the rotation for users with `prefers-reduced-motion: reduce`.
|
||||
|
||||
The variants module sits in its own file so an ejected source copy under `npx shadcn@latest add @suaveplan/spinner` keeps the cva config separate from the component logic, matching the upstream shadcn convention and easing per-project customisation without touching the JSX.
|
||||
|
||||
## Features
|
||||
|
||||
- Single `size` axis with three rungs (`sm` 16px, `default` 20px, `lg` 32px) using Tailwind `size-*`.
|
||||
- Border thickness scales with size — 2px for `sm`/`default`, 3px for `lg` — keeping the arc visually proportional.
|
||||
- `motion-safe:animate-spin` + `motion-reduce:animate-none` so reduced-motion users see a static circle.
|
||||
- Border colour inherits via `border-current` so consumers can recolour the spinner with a single `text-*` utility.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```tsx
|
||||
import { spinnerVariants } from "@suaveplan/react-ui";
|
||||
|
||||
const small = spinnerVariants({ size: "sm" });
|
||||
const large = spinnerVariants({ size: "lg" });
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```tsx
|
||||
import { cva } from "class-variance-authority";
|
||||
import { spinnerVariants } from "@suaveplan/react-ui";
|
||||
|
||||
const customSpinner = cva(
|
||||
[spinnerVariants({ size: "default" }), "text-emerald-500"].join(" ")
|
||||
);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `spinnerVariants(opts?)`
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `size` | `"sm" \| "default" \| "lg"` | `"default"` | Diameter — uses Tailwind `size-*` to set width and height together. |
|
||||
|
||||
Returns the merged class string.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The `border-t-transparent` rule is what creates the "missing arc" illusion that reads as motion when the element rotates. Without it the spinner would be a solid ring with no visual rotation.
|
||||
- `text-foreground/60` is applied at the cva level so the spinner renders with a subtle muted colour by default. Consumers override with any `text-*` utility (`text-primary`, `text-emerald-500`, etc.).
|
||||
- The spinner deliberately does not register `display`-related classes — `inline-block` is hard-coded so the spinner sits inline with adjacent text inside a Button without being affected by the Button's `flex` context.
|
||||
Reference in New Issue
Block a user