# Build a site using Masterpiece UI

Start from this website. No repository checkout or registry publication is required.
Resolve root-relative URLs against the website origin. Read `/ai/manifest.json` first;
it identifies the current package, SHA-256 checksum, catalog, recipes, and guides.
Keep the downloaded artifact with your project so future installs do not depend on
this website retaining an older release.

## RTL support

RTL support is partial across the catalog. Set `lang` and `dir` on the document
(for example, `<html lang="ar" dir="rtl">`) and load an appropriate font.
Composition layouts use logical spacing; card and rail arrows follow inherited
direction, including nested LTR content. Translate control labels using their props.
The blocks catalog still contains physical spacing and positioning utilities:
inspect each selected block at mobile and desktop widths before promising RTL parity.

For `HeroFullscreenImage`, prefer `alignment="start"` / `"end"` and
`overlayDirection="start"` / `"end"`. Alignment's legacy `left` / `right` values
alias start/end; use `physical-left` / `physical-right` for fixed physical alignment.
Overlay's legacy `left` / `right` values remain physical sides for compatibility.
The overlay direction names the opaque side of the image, not the fade destination.

## Styling rules to read before writing a page

The package ships precompiled CSS. **It does not install or run Tailwind in your
application.** Arbitrary utility classes you invent in JSX are not generated by
that stylesheet. Use component props, composition tokens, and ordinary CSS in
`src/site/site.css`; configure your own Tailwind build if you want additional
utilities. Import `ic-blocks/styles.css` once. For example:

```tsx
<ContentGrid className="news-grid">{/* ContentCard children */}</ContentGrid>
```

```css
/* src/site/site.css: this works without a Tailwind compiler. */
.site-grid.news-grid { gap: 2rem; }
@media (min-width: 64rem) {
  .site-grid.news-grid { grid-template-columns: 2fr 1fr 1fr; }
}
```

For library blocks in a dark section, use a nested, explicitly bridged palette;
see [the dark-section example below](#dark-sections-containing-library-blocks).
Section surface tones and a complete library-block palette are different controls.

## Layout wrappers and card specificity

`SectionLayout` always renders an inner `.site-container`. Its `className`, `style`,
and other HTML props apply to the outer `<section>`. Even `width="full"` keeps the
inner wrapper and sets its width to 100%:

```html
<section class="site-section your-section">
  <div class="site-container" data-width="full">Your children</div>
</section>
```

Put `ContentGrid` or `SplitContent` inside the section to lay out the children.
For an explicit CSS override, target `.your-section > .site-container`.

`ContentCard` renders `data-card-tone` on its `<article>`. A rule such as
`.site-card[data-card-tone="muted"]` has specificity **0-2-0**; a single custom
class has **0-1-0** and loses even when loaded later. Prefer the `tone` prop or
site tokens. For a scoped override, match the actual element and attribute:

```tsx
<ContentCard className="guide-card" tone="muted" title="Field notes" />
```

```css
/* Load application CSS after ic-blocks/styles.css. Specificity: 0-3-0. */
.site-card.guide-card[data-card-tone="muted"] {
  background: var(--site-background);
  color: var(--site-foreground);
}
```

See [composition](COMPOSITION.md#wrappers-and-css-overrides) for the DOM contract.

## Start a new project

Use Node 22.22.2 (Node >=22.12). Replace SITE below with the origin of this website.
Run this in a parent directory where `my-site` does not yet exist. These commands
use POSIX shell syntax; the same download, checksum, and Node steps work on other
platforms with their native equivalents.

```sh
SITE="https://YOUR-MASTERPIECE-UI-HOST"
mkdir -p masterpiece-download
curl --fail --location "$SITE/ai/manifest.json" -o masterpiece-download/manifest.json
SITE="$SITE" node --input-type=module <<'JS'
import fs from 'node:fs/promises'
import {createHash} from 'node:crypto'
const manifest = JSON.parse(await fs.readFile('masterpiece-download/manifest.json', 'utf8'))
const response = await fetch(new URL(manifest.package.url, process.env.SITE))
if (!response.ok) throw new Error(`Package download failed: ${response.status}`)
const bytes = Buffer.from(await response.arrayBuffer())
if (createHash('sha256').update(bytes).digest('hex') !== manifest.package.sha256) {
  throw new Error('Package checksum mismatch. Stop and fetch the manifest again.')
}
if (!/^[0-9A-Za-z.+-]+$/.test(manifest.package.version)) throw new Error('Invalid package version')
const filename = `ic-blocks-${manifest.package.version}.tgz`
await fs.writeFile('masterpiece-download/'+filename, bytes)
await fs.writeFile('masterpiece-download/package-file.txt', filename)
JS
PACKAGE_FILE=$(cat masterpiece-download/package-file.txt)
mkdir -p masterpiece-download/unpacked
tar -xzf "masterpiece-download/$PACKAGE_FILE" -C masterpiece-download/unpacked
node masterpiece-download/unpacked/package/tooling/cli.mjs recipes --json
node masterpiece-download/unpacked/package/tooling/cli.mjs create ./my-site --framework astro --recipe local-services --library "./masterpiece-download/$PACKAGE_FILE" --json
mkdir -p my-site/vendor
cp "masterpiece-download/$PACKAGE_FILE" "my-site/vendor/$PACKAGE_FILE"
cd my-site
npm install "./vendor/$PACKAGE_FILE"
npm run check
NODE_OPTIONS=--max-old-space-size=2048 npm run build
npm run dev
```

The unpacked CLI uses only Node built-ins; you can run it before installing the
package's React dependencies. Keep the versioned tarball in `vendor/` and the generated lockfile
in version control. Run the installed CLI directly with Node from the project root:

```sh
node ./node_modules/ic-blocks/tooling/cli.mjs search "hero" --limit 5 --json
node ./node_modules/ic-blocks/tooling/cli.mjs describe heroes/HeroFullscreenImage --json
```

This avoids `npx`/`npm exec` configuration resolution, including the error
`config prefix cannot be changed from project config`. The scaffold writes only
`install-links=true` to `.npmrc`; do not add a project-level `prefix` setting.
You can also keep using the unpacked CLI outside the project.

Choose `astro`, `next`, `vite`, or `gatsby`. Recipes are `local-services`,
`editorial-portfolio`, and `software-product`. Each creates four editable routes.
The CLI writes files only; it does not install, deploy, or connect forms. It refuses
to overwrite an existing destination. Gatsby builds should also set `GATSBY_CPU_COUNT=1`.
Run builds sequentially and stop unused preview servers.

## Find the right components

1. GET `/api/ai/search?q=fullscreen%20image%20hero&limit=5`.
2. Follow the `contract` URL of a selected result. It contains the exact import,
   props, required flags, nested shapes, dependencies, and React boundary guidance.
3. Use those names and props. Do not infer a prop from its name or from a screenshot.
4. Browse `/docs` to inspect the visual variants before composing a page.

Search is lexical, not visual similarity. It ignores common filler words, recognizes
phrases such as "call to action" as "CTA", and prioritizes the corresponding section
category. Exact component names rank first. Inspect the returned `terms` and
`inferredCategory` to see how the query was interpreted. If the results are too broad,
choose an explicit category:

```sh
node ./node_modules/ic-blocks/tooling/cli.mjs search "image background" --category cta --limit 5 --json
```

The equivalent HTTP request is `/api/ai/search?q=image%20background&category=cta&limit=5`.
Use `category=buttons` when you actually want a button, rather than a whole CTA section.
Describe the selected result before composing it; ranking does not certify a visual match.

`limit` is an integer from 1 to 50;
`q` is 1–200 characters. An optional `category` exactly matches an entry's category.
For offline search use the release's `catalog` URL in the manifest. That index lists
every public entry and links to individual contracts; avoid loading all contracts.
The downloaded CLI has the same source catalog. Some exports are primitives or
constants rather than full sections. Runtime defaults and TypeScript-requiredness
are separate. Types marked `truncated` need inspection of the installed declarations.
Functions, icons, and callbacks must stay inside a hydrated React wrapper.

## Customize and verify

Edit `site.config.json` for branding, navigation and contact information,
`src/site/content.ts` for shared copy, and `src/site/pages/` for page layouts.
When adding routes, add the host framework's route files too. Replace sample artwork
and all sample content. The contact email is a placeholder, not a connected form.
Run type checks, build, and review each route on desktop/mobile and in light/dark mode.
Connect and test any requested backend separately.

## Dark sections containing library blocks

`SectionLayout tone="inverse"` changes a composition surface. The bridge remaps some
library colors on that surface, but other tokens can still come from the outer light
palette. A complete nested `SiteTheme` with `bridgeLibraryTheme` makes the intended
library-block colors explicit without changing the rest of the page or global dark mode.

For example, create `src/site/DarkFooter.tsx` using this full CSS-color palette:

```tsx
'use client'
import {SiteTheme, SectionLayout, type SiteTokens} from 'ic-blocks/composition'
import {Footer5ColExpanded} from 'ic-blocks/footers'

const darkTokens = {
  background: '#0f172a', foreground: '#f8fafc',
  card: '#1e293b', muted: '#1e293b', mutedForeground: '#cbd5e1',
  border: '#475569', accent: '#f8fafc', accentForeground: '#0f172a',
  inverse: '#0f172a', inverseForeground: '#f8fafc', inverseMuted: '#cbd5e1',
  destructiveText: '#fca5a5',
} satisfies SiteTokens

export default function DarkFooter() {
  return (
    <SiteTheme tokens={darkTokens} bridgeLibraryTheme>
      <SectionLayout tone="inverse" spacing="none" width="full">
        <Footer5ColExpanded
          logo={{text: 'Your organization', href: '/'}}
          tagline="Replace this with your organization’s description."
          columns={[
            {title: 'Explore', links: [{label: 'About', href: '/about'}]},
            {title: 'Connect', links: [{label: 'Contact', href: '/contact'}]},
          ]}
        />
      </SectionLayout>
    </SiteTheme>
  )
}
```

Render `<DarkFooter />` inside your existing site theme, replacing the starter footer
in `Shell.tsx` rather than adding a second footer to a page. The same nested-theme
pattern works for newsletters, CTAs, and other blocks using library color tokens.
Blocks with fixed colors, images, or their own theme props need their own settings;
check the contract and rendered result. Use full CSS colors, not bare HSL channels,
and check the page in both OS color modes. See [theming](THEMING.md) for the token map.

## Editorial layouts and larger navigation

For viewport-sized menus, suggested-search dialogs, full-bleed image panels, and
horizontal highlights, describe `composition/SiteMegaMenu`,
`composition/SiteSearchDialog`, `composition/MediaOverlay`, and
`composition/ContentRail`. See [composition examples](COMPOSITION.md#editorial-navigation-and-media)
and the interactive page at `/demo/editorial-layouts`. Search needs your results
endpoint; font tokens need fonts loaded by your application. Match measured
container, gutter, typography, and crop values with scoped tokens and props.

## Update an existing installation

Each changed distributable now has a content-derived npm version such as
`0.1.0-build.h…` and a matching tarball filename. These versions identify exact
artifacts; use the manifest to select the current release rather than sorting
hash versions or using an npm range. Keep the existing lockfile and application
files. **Do not overwrite a fixed `vendor/ic-blocks.tgz` path and expect plain
`npm install` to refresh it.** The lockfile may still resolve the previous bytes.
This procedure also migrates installations of the old unversioned `0.1.0` package.

Run this from the consuming project's root, with SITE set to this website's origin:

```sh
SITE="https://YOUR-MASTERPIECE-UI-HOST"
SITE="$SITE" node --input-type=module <<'JS'
import fs from 'node:fs/promises'
import {createHash} from 'node:crypto'
import {execFileSync} from 'node:child_process'
const response = await fetch(new URL('/ai/manifest.json', process.env.SITE))
if (!response.ok) throw new Error(`Manifest download failed: ${response.status}`)
const manifest = await response.json()
if (!/^[0-9A-Za-z.+-]+$/.test(manifest.package.version)) throw new Error('Invalid package version')
const archive = await fetch(new URL(manifest.package.url, process.env.SITE))
if (!archive.ok) throw new Error(`Package download failed: ${archive.status}`)
const bytes = Buffer.from(await archive.arrayBuffer())
if (createHash('sha256').update(bytes).digest('hex') !== manifest.package.sha256) throw new Error('Package checksum mismatch')
await fs.mkdir('vendor', {recursive:true})
const filename = `ic-blocks-${manifest.package.version}.tgz`
await fs.writeFile('vendor/'+filename, bytes)
execFileSync('npm', ['install', './vendor/'+filename, '--no-audit', '--no-fund'], {stdio:'inherit'})
const installed = JSON.parse(await fs.readFile('node_modules/ic-blocks/package.json', 'utf8'))
if (installed.version !== manifest.package.version) throw new Error('Installed package version does not match the release')
console.log(`Installed ${installed.name}@${installed.version}`)
JS
npm run check
npm run build
```

Commit the new tarball, `package.json`, and `package-lock.json`. Review the lockfile
diff and test the site before removing old vendor files. Re-running `npm install`
or `npm ci` will then resolve the selected artifact. Restart a running dev server
so it reloads the package. A failed install or version check means the update is
incomplete; do not delete the whole lockfile as a workaround.

## Add to an existing site

Download and verify the package as above, keep its versioned filename in `vendor/`,
and run `npm install "./vendor/$PACKAGE_FILE"` from the existing project. Import
`ic-blocks/styles.css` once in the app. Use category imports such as
`import {HeroFullscreenImage} from 'ic-blocks/heroes'` and follow the
[framework guide](FRAMEWORKS.md) for hydration and provider setup.
Do not run `create` into an existing project.

Read [composition](COMPOSITION.md) and [theming](THEMING.md) for layout and brand tokens.
This is a React library: Vue, Svelte and Angular can host React islands, but these
are not native components for those frameworks. There is no MCP server required
or provided by this HTTP workflow.

## Match a designer's component brief

Read the catalog's category scope (`purpose`, `use_when`, `avoid_when`) before
selecting candidates. Use `documented` membership from that release, not current
HTML membership. Contract `selection` records source-reviewed layout, behavior,
slots and limitations; null means not reviewed. Shared prop types do not prove
that a variant implements every prop. Read [selection metadata](SELECTION-METADATA.md).

Prefer `contract.preview.url` at its listed desktop/mobile sizes. These previews
use that release's built package and demo props. Follow `manifest.releaseManifest`
to record an exact manifest; save the tarball and its checksum because old releases
are not guaranteed to remain hosted. Preview samples and checks do not certify the
finished client's site.

## Selection and migration notes

- `FeatureGridIconsLeft` uses `columns={2|3|4}` (default 3) and heading
  `alignment="left"|"center"`; left follows reading direction. Its earlier fixed
  single-column layout was a defect.
- `SidebarTree` renders supplied icons, opens active ancestors, and recursively
  filters navigation when `search` is supplied. It is a fixed-width desktop shell;
  choose a mobile frame separately. Group hrefs are unique identifiers; leaves navigate.
- `AnnouncementBar` now defaults to document flow and brand tokens. Select
  `position="sticky"` or `"fixed"` deliberately; reserve space for fixed bars.
- `PricingThreeTier` hides the billing switch without a supplied annual price and
  displays savings only when `yearlyDiscount` is explicitly supplied. Annual zero
  values work. A period may be `mo` or `/mo`; only one slash is rendered.
- `TestimonialReviewStars` requires real data for verification labels and dates.
  Supply `testimonials[].verificationLabel` only when warranted. Summary values
  derive from supplied reviews unless authoritative totals are passed explicitly.
- `ServiceAreaGrid` displays every supplied ZIP code and filters by coverage.
- `CodeBlockWithCopy.language` labels code; it does not load a syntax engine.
  Provide `renderCode(code, language)` returning React nodes for host highlighting;
  the copy button always copies the original string.
- Search for `version switcher` reports an explicit capability gap. `VersionBadge`
  displays a release type and does not switch documentation versions.

## Download contents and account access

The tarball includes compiled components, declarations, CSS, the CLI, recipes,
contracts, guides, editable component source in `source/`, and sample SVG artwork
in `assets/`. Read `source/README.md` before editing; utility classes you add need
your own CSS or Tailwind pipeline. Copy desired `assets/previews` files into your
site’s `public/previews`. `assets/manifest.json` includes file checksums and origin.
Client artwork, third-party fonts, and external demo images are not included.

Public downloads remain available during the transition. Licensed accounts can
download through the account page without repository access. Agents can use the
manifest’s `downloads.url` with `Authorization: Bearer <license-key>`, supplied by
a secret environment variable. Never put credentials in a URL or commit them.
Verify the result against `package.sha256` before installing. This endpoint serves
the release on the current deployment; a 409 means refresh the manifest. Retain
the tarball for future installs. Netlify deployment storage is not a permanent
release archive.

## Plan the visitor tasks before choosing components

The manifest's `planning` resource links compact page/profile indexes and individual contracts.
Use [page planning](PAGE-PLANNING.md) to retrieve a business profile, then only the page definitions
needed for the request. The bundled CLI also supports `planning profiles`,
`planning pages restaurant-menu` and `planning briefs food-menu-categorized`.

These are definitions and briefs, not plan-driven generators. Read each contract's independent
readiness fields and missing capabilities. Forty-three inherited definitions still need review.
Required information may be a section; conditional offerings need actual client facts. Preserve
unsupported requests as visible work, and keep the client's design direction and existing URLs.
