Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/camp-dev-rename.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@camp-dev/bones": minor
---

Rename `@lovo/bones` to `@camp-dev/bones` and split it into subpath exports: the framework-agnostic core at the package root (`boneAttributes`, `minMax`, `resolveLength`), the React API at `@camp-dev/bones/react`, and the stylesheet at `@camp-dev/bones/css`. Update imports: `@lovo/bones` → `@camp-dev/bones/react`, `@lovo/bones/css` → `@camp-dev/bones/css`. `react` and `react-dom` are now optional peer dependencies.
2 changes: 1 addition & 1 deletion .changeset/config.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"$schema": "https://unpkg.com/@changesets/config@3.1.1/schema.json",
"changelog": ["@changesets/changelog-github", { "repo": "lovo-hq/bones" }],
"changelog": ["@changesets/changelog-github", { "repo": "campdotdev/bones" }],
"commit": false,
"fixed": [],
"linked": [],
Expand Down
19 changes: 11 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ on:
branches:
- main

permissions:
contents: read

jobs:
# ── Bones library ──────────────────────────────────────────
bones-check:
Expand All @@ -21,7 +24,7 @@ jobs:
cache: true

- name: Run lint, format, and type checks
run: vp run --filter @lovo/bones check
run: vp run --filter @camp-dev/bones check
Comment thread
coderabbitai[bot] marked this conversation as resolved.

bones-build:
name: "Bones: Build"
Expand All @@ -36,7 +39,7 @@ jobs:
cache: true

- name: Build library
run: vp run --filter @lovo/bones build
run: vp run --filter @camp-dev/bones build

bones-test:
name: "Bones: Test"
Expand All @@ -51,7 +54,7 @@ jobs:
cache: true

- name: Run tests
run: vp run --filter @lovo/bones test -- --run
run: vp run --filter @camp-dev/bones test -- --run

# ── Demo app ───────────────────────────────────────────────
demo-check:
Expand All @@ -67,7 +70,7 @@ jobs:
cache: true

- name: Build bones library
run: vp run --filter @lovo/bones build
run: vp run --filter @camp-dev/bones build

- name: Run type check
run: vp run --filter bones-demo check
Expand All @@ -85,7 +88,7 @@ jobs:
cache: true

- name: Build bones library
run: vp run --filter @lovo/bones build
run: vp run --filter @camp-dev/bones build

- name: Build demo app
run: vp run --filter bones-demo build
Expand All @@ -103,7 +106,7 @@ jobs:
cache: true

- name: Build bones library
run: vp run --filter @lovo/bones build
run: vp run --filter @camp-dev/bones build

- name: Run tests
run: vp run --filter bones-demo test -- --run
Expand All @@ -122,7 +125,7 @@ jobs:
cache: true

- name: Build bones library
run: vp run --filter @lovo/bones build
run: vp run --filter @camp-dev/bones build

- name: Run type check
run: vp run --filter bones-docs check
Expand All @@ -140,7 +143,7 @@ jobs:
cache: true

- name: Build bones library
run: vp run --filter @lovo/bones build
run: vp run --filter @camp-dev/bones build

- name: Build docs site
run: vp run --filter bones-docs build
24 changes: 17 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Bones

[![Bundle Size](https://deno.bundlejs.com/badge?q=@lovo/bones)](https://bundlejs.com/?q=%40lovo%2Fbones)
[![Bundle Size](https://deno.bundlejs.com/badge?q=@camp-dev/bones)](https://bundlejs.com/?q=%40camp-dev%2Fbones)

Skeleton loaders designed for React Server Components and streaming.

Expand All @@ -21,21 +21,31 @@ Bones skips the duplication. You write your markup once and it handles both stat
## Installation

```bash
npm install @lovo/bones
npm install @camp-dev/bones
```

Import the CSS once in your root layout or entry point:

```tsx
import "@lovo/bones/css";
import "@camp-dev/bones/css";
```

## Entry points

| Import | Contents |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------- |
| `@camp-dev/bones/react` | `createBones`, `readPromise`, `forceBones`, `minMax`, `<Bones>`, `<BonesForce>` |
| `@camp-dev/bones/css` | The skeleton stylesheet. Import once in your root layout. |
| `@camp-dev/bones` | The framework-agnostic core (`boneAttributes`, `minMax`). You only need this to build your own renderer or adapter. |

React is an optional peer dependency: installing the package without React is supported and only the `/react` entry requires it.

## Basic usage

Pass data (or a promise of data) to `createBones`. Spread the `bone` function's return value onto elements that should show skeletons while loading.

```tsx
import { createBones } from "@lovo/bones";
import { createBones } from "@camp-dev/bones/react";

function ProfileCard({ user }: { user: Promise<User> | User }) {
const { bone, data, lines } = createBones(user);
Expand All @@ -55,7 +65,7 @@ function ProfileCard({ user }: { user: Promise<User> | User }) {
Wrap components that receive promises in `<Bones>`. It creates a Suspense boundary and generates the skeleton fallback for you:

```tsx
import { Bones } from "@lovo/bones";
import { Bones } from "@camp-dev/bones/react";

export default function Page() {
return (
Expand All @@ -81,15 +91,15 @@ While the promise is pending, `<Bones>` renders the same `<ProfileCard>` tree wi
Use `forceBones` to see a component's skeleton state without setting up real data:

```tsx
import { createBones, forceBones } from "@lovo/bones";
import { createBones, forceBones } from "@camp-dev/bones/react";

<ProfileCard user={forceBones} />;
```

To force an entire subtree into skeleton mode at once, wrap it with `<BonesForce>`:

```tsx
import { BonesForce } from "@lovo/bones";
import { BonesForce } from "@camp-dev/bones/react";

<BonesForce>
<ProfileCard />
Expand Down
2 changes: 1 addition & 1 deletion apps/demo/app/compare/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { BonesForce } from "bones";
import { BonesForce } from "bones/react";
import { HeroSection } from "@/components/hero-section/hero-section";
import { DemoSection } from "@/components/demo-section/demo-section";
import { PokemonGrid } from "@/components/pokemon-grid/pokemon-grid";
Expand Down
2 changes: 1 addition & 1 deletion apps/demo/app/compare/pokemon/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { BonesForce, forceBones } from "bones";
import { BonesForce, forceBones } from "bones/react";
import Link from "next/link";
import { PokemonHero } from "@/components/pokemon-hero/pokemon-hero";
import { BaseStatsCard } from "@/components/base-stats-card/base-stats-card";
Expand Down
2 changes: 1 addition & 1 deletion apps/demo/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Metadata } from "next";
import { cookies, headers } from "next/headers";
import { BonesForce } from "bones";
import { BonesForce } from "bones/react";
import { BonesDevTool } from "@/components/bones-devtool/bones-devtool";
import "bones/css";
import "./globals.css";
Expand Down
2 changes: 1 addition & 1 deletion apps/demo/app/pokemon/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Bones } from "bones";
import { Bones } from "bones/react";
import Link from "next/link";
import { cookies } from "next/headers";
import { delay } from "@/lib/delay";
Expand Down
2 changes: 1 addition & 1 deletion apps/demo/app/pokemon/[id]/tabs-section.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Bones } from "bones";
import { Bones } from "bones/react";
import type { PokemonMoveEntry, MoveDetail, EncounterLocation } from "@/lib/pokeapi";
import { DetailTabs } from "@/components/detail-tabs/detail-tabs";
import { MovesPanel } from "@/components/moves-panel/moves-panel";
Expand Down
2 changes: 1 addition & 1 deletion apps/demo/components/animations-demo/animations-demo.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { BonesForce } from "bones";
import { BonesForce } from "bones/react";
import { DemoSection } from "@/components/demo-section/demo-section";
import { PokemonCard } from "@/components/pokemon-card/pokemon-card";
import styles from "./styles.module.css";
Expand Down
2 changes: 1 addition & 1 deletion apps/demo/components/article-preview/article-preview.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createBones } from "bones";
import { createBones } from "bones/react";
import styles from "./styles.module.css";

interface Article {
Expand Down
2 changes: 1 addition & 1 deletion apps/demo/components/base-stats-card/base-stats-card.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createBones } from "bones";
import { createBones } from "bones/react";
import type { PokemonData } from "@/lib/pokeapi";
import styles from "./styles.module.css";

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createBones, minMax } from "bones";
import { createBones, minMax } from "bones/react";
import styles from "./styles.module.css";

const VERSION_TO_GENERATION: Record<string, string> = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Image from "next/image";
import { createBones } from "bones";
import { createBones } from "bones/react";
import type { EvolutionChain } from "@/lib/pokeapi";
import styles from "./styles.module.css";

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { BonesForce } from "bones";
import { BonesForce } from "bones/react";
import { fetchPokemonList } from "@/lib/pokeapi";
import { DemoSection } from "@/components/demo-section/demo-section";
import { PokemonGrid } from "@/components/pokemon-grid/pokemon-grid";
Expand Down
2 changes: 1 addition & 1 deletion apps/demo/components/hero-section/hero-section.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@ describe("HeroSection", () => {
const docsLink = screen.getByRole("link", { name: "Docs" });
const githubLink = screen.getByRole("link", { name: "GitHub" });
expect(docsLink.getAttribute("href")).toBe("https://bones.lovo.sh");
expect(githubLink.getAttribute("href")).toBe("https://github.com/lovo-hq/bones");
expect(githubLink.getAttribute("href")).toBe("https://github.com/campdotdev/bones");
});
});
2 changes: 1 addition & 1 deletion apps/demo/components/hero-section/hero-section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export function HeroSection() {
</a>
<span className={styles.heroDot} aria-hidden="true" />
<a
href="https://github.com/lovo-hq/bones"
href="https://github.com/campdotdev/bones"
className={styles.heroLink}
target="_blank"
rel="noopener noreferrer"
Expand Down
2 changes: 1 addition & 1 deletion apps/demo/components/info-card/info-card.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createBones } from "bones";
import { createBones } from "bones/react";
import styles from "./styles.module.css";

interface InfoRow {
Expand Down
2 changes: 1 addition & 1 deletion apps/demo/components/locations-panel/locations-panel.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createBones } from "bones";
import { createBones } from "bones/react";
import type { EncounterLocation } from "@/lib/pokeapi";
import styles from "./styles.module.css";

Expand Down
2 changes: 1 addition & 1 deletion apps/demo/components/moves-panel/moves-interactive.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use client";

import { useState, useMemo } from "react";
import { createBones, minMax } from "bones";
import { createBones, minMax } from "bones/react";
import type { PokemonMoveEntry, MoveDetail } from "@/lib/pokeapi";
import { TypeBadge } from "@/components/type-badge/type-badge";
import styles from "./styles.module.css";
Expand Down
2 changes: 1 addition & 1 deletion apps/demo/components/moves-panel/moves-panel.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createBones } from "bones";
import { createBones } from "bones/react";
import type { PokemonMoveEntry, MoveDetail } from "@/lib/pokeapi";
import { MovesInteractive } from "./moves-interactive";

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { BonesForce } from "bones";
import { BonesForce } from "bones/react";
import { ArticlePreview } from "@/components/article-preview/article-preview";
import { DemoSection } from "@/components/demo-section/demo-section";
import styles from "./styles.module.css";
Expand Down
2 changes: 1 addition & 1 deletion apps/demo/components/pokemon-card/pokemon-card.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Image from "next/image";
import { createBones } from "bones";
import { createBones } from "bones/react";
import Link from "next/link";
import type { PokemonListItem } from "@/lib/pokeapi";
import { TypeBadge } from "@/components/type-badge/type-badge";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Image from "next/image";
import { createBones } from "bones";
import { createBones } from "bones/react";
import type { PokemonDetail } from "@/lib/pokeapi";
import { StatBar } from "@/components/stat-bar/stat-bar";
import { TypeBadge } from "@/components/type-badge/type-badge";
Expand Down
2 changes: 1 addition & 1 deletion apps/demo/components/pokemon-grid/pokemon-grid.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createBones } from "bones";
import { createBones } from "bones/react";
import { PokemonCard } from "@/components/pokemon-card/pokemon-card";
import type { PokemonListItem } from "@/lib/pokeapi";
import styles from "./styles.module.css";
Expand Down
2 changes: 1 addition & 1 deletion apps/demo/components/pokemon-hero/pokemon-hero.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Image from "next/image";
import { createBones } from "bones";
import { createBones } from "bones/react";
import type { PokemonData, SpeciesData } from "@/lib/pokeapi";
import { TypeBadge } from "@/components/type-badge/type-badge";
import styles from "./styles.module.css";
Expand Down
2 changes: 1 addition & 1 deletion apps/demo/components/stat-bar/stat-bar.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createBones } from "bones";
import { createBones } from "bones/react";
import styles from "./styles.module.css";

type Stat = { name: string; value: number };
Expand Down
2 changes: 1 addition & 1 deletion apps/demo/components/suspense-demo/suspense-demo.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Bones } from "bones";
import { Bones } from "bones/react";
import { delay } from "@/lib/delay";
import { fetchPokemonList } from "@/lib/pokeapi";
import { DemoSection } from "@/components/demo-section/demo-section";
Expand Down
6 changes: 3 additions & 3 deletions apps/demo/components/theming-demo/theming-demo.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { BonesForce } from "bones";
import { BonesForce } from "bones/react";
import { DemoSection } from "@/components/demo-section/demo-section";
import { PokemonCard } from "@/components/pokemon-card/pokemon-card";
import styles from "./styles.module.css";
Expand All @@ -9,8 +9,8 @@ export function ThemingDemo() {
title="Theming"
description={
<>
Customize skeleton colors with CSS custom properties. Override{" "}
<code>--bone-base</code> and <code>--bone-highlight</code> on any container.
Customize skeleton colors with CSS custom properties. Override <code>--bone-base</code>{" "}
and <code>--bone-highlight</code> on any container.
</>
}
>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createBones } from "bones";
import { createBones } from "bones/react";
import type { TypeDefenseMap } from "@/lib/pokeapi";
import { TypeBadge } from "@/components/type-badge/type-badge";
import styles from "./styles.module.css";
Expand Down
4 changes: 2 additions & 2 deletions apps/demo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
"private": true,
"scripts": {
"dev": "next dev",
"build": "pnpm --filter @lovo/bones build && next build",
"build": "pnpm --filter @camp-dev/bones build && next build",
"start": "next start",
"check": "tsc --noEmit",
"test": "vp test"
},
"dependencies": {
"bones": "workspace:@lovo/bones@*",
"bones": "workspace:@camp-dev/bones@*",
"lucide-react": "^1.11.0",
"next": "^15.3.1",
"react": "^19.1.0",
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export default function RootLayout({ children }: { children: ReactNode }) {
</svg>
),
text: "GitHub",
url: "https://github.com/lovo-hq/bones",
url: "https://github.com/campdotdev/bones",
external: true,
},
]}
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/components/demo/pokemon-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import { useState } from "react";
import Image from "next/image";
import { createBones } from "bones";
import { createBones } from "bones/react";
import styles from "./pokemon-card.module.css";

interface Pokemon {
Expand Down
4 changes: 2 additions & 2 deletions apps/docs/content/docs/api/bones-component.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ description: Suspense wrapper that automatically renders skeletons as the fallba
---

```tsx
import { Bones } from "@lovo/bones";
import { Bones } from "@camp-dev/bones/react";
```

`<Bones>` wraps its children in a `<Suspense>` boundary. The fallback is the same component tree, but with all `Promise` props replaced by `forceBones` and a loading flag set so nested `createBones` calls return skeleton attributes.

## Usage

```tsx
import { Bones } from "@lovo/bones";
import { Bones } from "@camp-dev/bones/react";

export default function Page() {
return (
Expand Down
Loading
Loading