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
4 changes: 0 additions & 4 deletions .npmrc

This file was deleted.

19 changes: 10 additions & 9 deletions apps/docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,21 @@ path from the built artifact. It does not run Next.js or a search backend.

Edit MDX in `content/docs/` and sidebar order in each `meta.json`. The build
checks local HTML links and assets, and emits a browser-searchable index at
`search.json`. Fonts come from the installed `@bloxwap/font` and `@bloxwap/hig-tokens` packages, so builds do not fetch Google Fonts.
`search.json`. Fonts come from the installed `@bloxwap/font` package (and `@bloxwap/hig-native-fonts` for social cards), so builds do not fetch Google Fonts.

The presentation follows the Bloxwap HIG and consumes it as packages, not copies:

- `@bloxwap/hig-tokens`, `@bloxwap/hig-contracts` and `@bloxwap/hig-react` are installed from the
pinned, unpublished candidate tarballs in [`vendor/bloxwap-hig/`](../../vendor/bloxwap-hig/README.md).
- `@bloxwap/hig-tokens`, `@bloxwap/hig-contracts` and `@bloxwap/hig-react` (plus the build-time
`@bloxwap/hig-native-fonts`) are installed from the pinned, unpublished candidate tarballs in [`vendor/bloxwap-hig/`](../../vendor/bloxwap-hig/README.md).
`app/global.css` imports `@bloxwap/hig-tokens/css` and `@bloxwap/hig-react/css` and maps HIG roles
onto Fumadocs; it does not declare palette values.
- `styles/hig-roles.generated.css` is generated from the package manifest by
`scripts/hig-tokens.mjs --write` (radius, type, motion and density roles the CSS distribution does not
emit yet). `npm run lint` fails on drift, tarball hash changes, copied palettes, `.btn`-style primitive
copies or font binaries in the docs source.
- Radius, type, motion and density roles (`--hig-radius-*`, `--hig-type-*`, `--hig-motion-*`,
`--hig-density-*`) and the font roles come from `@bloxwap/hig-tokens/css`; the docs declare none.
`npm run lint` (`scripts/hig-tokens.mjs --check`) fails on tarball hash changes, local role declarations
or retired role names, copied palettes, `.btn`-style primitive copies or font binaries in the docs source.
- Buttons, install tabs/copy, code-window copy, callouts, search error/empty states and the playground
loading status use HIG components (`Button`, `IconButton`, `Tabs`, `Surface`, `Callout`, `Alert`,
`EmptyState`, `Spinner`). The embedded demo keeps the SDK's own controls, themed from HIG roles.
`EmptyState`, `Spinner`) and the `InstallCommand`/`CodeWindow` recipe entries. The embedded demo keeps the SDK's own controls, themed from HIG roles.
- `public/logos/bloxwap-wordmark-white.svg` and `public/icon.svg` are the supplied
monorepo documentation artwork. Preserve the marks instead of typesetting them.

Expand Down Expand Up @@ -94,7 +94,8 @@ chrome's `--cts-font`/`--cts-mono` at Bloxwap Sans/Mono, and sets the canvas
`theme.fontFamily` to Bloxwap Sans and `monoFamily`/`scaleFontFamily` (status
line, drawing labels and scales) to Bloxwap Mono. The standalone playground and
SDK presets retain their existing font defaults.
Social cards use the static Bloxwap Sans faces shipped in `@bloxwap/hig-tokens/fonts` (OFL).
Social cards use the static Bloxwap Sans faces shipped in `@bloxwap/hig-native-fonts/fonts` (OFL), a
build-time devDependency; Satori cannot read the variable WOFF2 in `@bloxwap/font`.

HIG presentation, pinned candidate provenance, validation, exceptions and upgrade/rollback
instructions are recorded in [the adoption report](../../docs/hig-adoption/README.md).
53 changes: 25 additions & 28 deletions apps/docs/app/global.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
@import 'fumadocs-ui/css/preset.css';
@import '@bloxwap/hig-tokens/css';
@import '@bloxwap/hig-react/css';
@import '../styles/hig-roles.generated.css';

@theme inline {
--font-sans: var(--font-bloxwap-sans), ui-rounded, system-ui, sans-serif;
Expand Down Expand Up @@ -73,7 +72,7 @@ html > body[data-scroll-locked] {

#nd-page > p {
font-size: var(--hig-type-body-size);
line-height: var(--hig-type-body-line);
line-height: var(--hig-type-body-line-height);
}

.prose {
Expand Down Expand Up @@ -140,7 +139,7 @@ html > body[data-scroll-locked] {
min-height: 2.5rem;
padding: 0.5rem 0.75rem;
border-radius: var(--hig-radius-pill);
transition: background-color var(--hig-duration-fast) var(--ease-spring), color var(--hig-duration-fast) var(--ease-spring);
transition: background-color var(--hig-motion-duration-fast) var(--hig-motion-easing-standard), color var(--hig-motion-duration-fast) var(--hig-motion-easing-standard);
font-size: 0.875rem;
font-weight: 600;
}
Expand All @@ -151,22 +150,23 @@ html > body[data-scroll-locked] {
}

/*
* HIG controls (@bloxwap/hig-react/css) in the docs shell. The Next font loader hashes family names, so
* point HIG's 'Bloxwap Sans'/'Bloxwap Mono' at the loaded faces, and let links and icons share the button.
* Dense docs regions opt into HIG compact density on fine pointers; coarse pointers keep 48px targets.
* HIG controls (@bloxwap/hig-react/css) in the docs shell. HIG reads --hig-font-sans/--hig-font-mono, which
* resolve to the @bloxwap/font Next loader variables, so no family bridge is needed. Links and icons share the
* button. Dense docs regions opt into HIG compact density on fine pointers; coarse pointers keep 48px targets.
*/
.hig-button { display: inline-flex; align-items: center; justify-content: center; gap: var(--hig-space-sm); font-family: var(--font-sans); text-decoration: none; touch-action: manipulation; transition: filter var(--hig-duration-fast) var(--ease-spring), transform var(--hig-duration-fast) var(--ease-spring); }
.hig-button { display: inline-flex; align-items: center; justify-content: center; gap: var(--hig-space-sm); text-decoration: none; touch-action: manipulation; transition: filter var(--hig-motion-duration-fast) var(--hig-motion-easing-standard), transform var(--hig-motion-duration-fast) var(--hig-motion-easing-standard); }
.hig-button svg { flex: none; width: 1rem; height: 1rem; }
.hig-button > span[aria-hidden='true'] { display: contents; }
.hig-surface pre { font-family: var(--font-mono); }
[data-hig-density='compact'] { --hig-control: var(--hig-control-compact); --hig-gap: var(--hig-space-sm); }
[data-hig-density='compact'] { --hig-control: var(--hig-density-compact-control); --hig-gap: var(--hig-space-sm); }
[data-hig-density='compact'] .hig-button { padding: 0 var(--hig-space-md); font-size: var(--hig-type-label-size); }
.docs-icon-button.hig-button { position: relative; width: var(--hig-control); padding: 0; }
/* HIG busy text stays announced but is visually hidden inside icon-only buttons. */
.docs-icon-button .hig-busy { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.copy-feedback { color: var(--hig-negative); font-size: var(--hig-type-caption-size); }
@media (pointer: coarse) {
[data-hig-density='compact'] { --hig-control: var(--hig-control-comfortable); }
/* HIG buttons are 44px (--hig-button) by default; touch keeps the comfortable 48px target role. */
[data-hig-density='compact'] { --hig-control: var(--hig-density-comfortable-control); --hig-button: var(--hig-density-comfortable-target); }
}

/* Chart-specific layout on the scale above; typography, surfaces, and states follow workers/docs. */
Expand Down Expand Up @@ -210,7 +210,7 @@ html > body[data-scroll-locked] {
.window-dots { display: flex; gap: var(--hig-space-xs); }
.window-dots > i { width: 6px; height: 6px; border-radius: var(--hig-radius-pill); background: var(--hig-nested); }
.code-actions { display: flex; align-items: center; gap: var(--hig-space-xs); margin-right: calc(-1 * var(--hig-space-md)); }
.code-toolbar { min-height: var(--hig-control-compact); }
.code-toolbar { min-height: var(--hig-density-compact-control); }
/* HIG CodeWindow recipe inside the hero card: the card supplies the surface, the recipe its language label and code. */
.code-window > .hig-surface { padding: 0; border-radius: 0; background: transparent; }
.code-window > .hig-surface > .hig-text { margin: 0; padding: var(--hig-space-sm) var(--hig-space-xxl) 0; font: var(--text-2xs) / 1.5 var(--font-mono); color: color-mix(in oklab, var(--hig-info) 60%, var(--hig-text)); }
Expand All @@ -233,7 +233,7 @@ html > body[data-scroll-locked] {
@keyframes landing-rise {
from { opacity: 0; translate: 0 8px; }
}
.hero-copy > *, .code-window, .hero-preview { animation: landing-rise 600ms var(--ease-spring) both; }
.hero-copy > *, .code-window, .hero-preview { animation: landing-rise 600ms var(--hig-motion-easing-standard) both; }
.hero-copy > h1 { animation-delay: 60ms; }
.hero-copy > :not(.eyebrow, h1) { animation-delay: 200ms; }
.code-window { animation-delay: 360ms; }
Expand All @@ -244,9 +244,9 @@ html > body[data-scroll-locked] {

/* Search (header and docs sidebar): a zero-stroke filled pill at the small control height, with key chips one step lighter than the box. */
button[data-search-full] {
height: var(--hig-control-compact); gap: var(--hig-space-sm); padding: 0 var(--hig-space-xs) 0 var(--hig-space-md);
height: var(--hig-density-compact-control); gap: var(--hig-space-sm); padding: 0 var(--hig-space-xs) 0 var(--hig-space-md);
border: 0; border-radius: var(--hig-radius-pill); background: var(--hig-surface); color: var(--hig-muted); font-size: var(--text-sm);
transition: background-color var(--hig-duration-fast) var(--ease-spring), color var(--hig-duration-fast) var(--ease-spring);
transition: background-color var(--hig-motion-duration-fast) var(--hig-motion-easing-standard), color var(--hig-motion-duration-fast) var(--hig-motion-easing-standard);
}
button[data-search-full]:hover { background: var(--hig-nested); color: var(--hig-text); }
button[data-search-full] kbd {
Expand Down Expand Up @@ -300,28 +300,28 @@ button[data-search-full] kbd {
.guide-links { display: grid; gap: var(--hig-space-md); }
.guide-links a {
display: flex; align-items: center; gap: var(--hig-space-xl);
transition: background-color var(--hig-duration-fast) var(--ease-spring);
transition: background-color var(--hig-motion-duration-fast) var(--hig-motion-easing-standard);
}
.guide-links a:hover { background: var(--hig-nested); }
.guide-links a > span { font: var(--text-2xs) var(--font-mono); color: var(--color-fd-muted-foreground); }
.guide-links a > svg { flex: none; margin-left: auto; width: 1rem; height: 1rem; color: var(--color-fd-muted-foreground); transition: color var(--hig-duration-fast) var(--ease-spring), transform var(--hig-duration-fast) var(--ease-spring); }
.guide-links a > svg { flex: none; margin-left: auto; width: 1rem; height: 1rem; color: var(--color-fd-muted-foreground); transition: color var(--hig-motion-duration-fast) var(--hig-motion-easing-standard), transform var(--hig-motion-duration-fast) var(--hig-motion-easing-standard); }
.guide-links a:hover > svg { color: var(--color-fd-primary); transform: translateX(2px); }
.guide-links h3 { font-size: var(--text-sm); margin-bottom: var(--hig-space-xs); font-weight: 600; transition: color var(--hig-duration-fast) var(--ease-spring); }
.guide-links h3 { font-size: var(--text-sm); margin-bottom: var(--hig-space-xs); font-weight: 600; transition: color var(--hig-motion-duration-fast) var(--hig-motion-easing-standard); }
.guide-links a:hover h3 { color: var(--color-fd-primary); }
.guide-links p { color: var(--color-fd-muted-foreground); font-size: var(--text-xs); }
/* Full-bleed rule; content inset to the header's box so both edges line up with the nav. */
/* The bloxwap.com footer (monorepo workers/www), inset to the header's box. The home page ends with it. */
.site-footer { margin-top: auto; padding: var(--hig-space-section) max(var(--hig-space-lg), calc((100% - var(--fd-layout-width, 1400px)) / 2 + var(--hig-space-lg))) var(--hig-space-section); }
/* Page end: Edit on GitHub as a quiet ghost pill, prev/next as zero-stroke fill cards (the landing's card style). */
#nd-page .edit-on-github {
align-self: flex-start; height: var(--hig-control-compact); padding: 0 var(--hig-space-md); border: 0; border-radius: var(--hig-radius-pill);
align-self: flex-start; height: var(--hig-density-compact-control); padding: 0 var(--hig-space-md); border: 0; border-radius: var(--hig-radius-pill);
background: transparent; color: var(--hig-muted); font-size: var(--text-sm);
transition: background-color var(--hig-duration-fast) var(--ease-spring), color var(--hig-duration-fast) var(--ease-spring);
transition: background-color var(--hig-motion-duration-fast) var(--hig-motion-easing-standard), color var(--hig-motion-duration-fast) var(--hig-motion-easing-standard);
}
#nd-page .edit-on-github:hover { background: var(--hig-surface); color: var(--hig-text); }
.page-nav > a {
padding: var(--hig-space-xl) var(--hig-space-xxl); border: 0; border-radius: var(--hig-radius-xl); background: var(--hig-surface);
transition: background-color var(--hig-duration-fast) var(--ease-spring), color var(--hig-duration-fast) var(--ease-spring);
transition: background-color var(--hig-motion-duration-fast) var(--hig-motion-easing-standard), color var(--hig-motion-duration-fast) var(--hig-motion-easing-standard);
}
.page-nav > a:hover { background: var(--hig-nested); color: var(--hig-action); }
.footer-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); column-gap: var(--hig-space-xxl); row-gap: var(--hig-space-section); }
Expand All @@ -332,7 +332,7 @@ button[data-search-full] kbd {
/* The wordmark's box reserves descender room for the "p"; shift it so the cap height (not the box) centers on the icon: (143/2 - (149-74.6)/2) / 223.6 of the viewBox. */
.footer-wordmark { width: auto; height: 32px; transform: translateY(15.3%); }
.footer-col h2 { margin: 0 0 var(--hig-space-md); color: var(--color-fd-muted-foreground); font: 700 var(--text-2xs) / 1.5 var(--font-sans); letter-spacing: .14em; text-transform: uppercase; }
.footer-col a, .footer-base a { transition: color var(--hig-duration-fast) var(--ease-spring), opacity var(--hig-duration-fast) var(--ease-spring); }
.footer-col a, .footer-base a { transition: color var(--hig-motion-duration-fast) var(--hig-motion-easing-standard), opacity var(--hig-motion-duration-fast) var(--hig-motion-easing-standard); }
.footer-col a { display: block; width: fit-content; padding: var(--hig-space-xs) 0; color: var(--color-fd-foreground); font-size: var(--text-sm); opacity: .8; }
.footer-col a:hover, .footer-base a:hover { color: var(--color-fd-primary); opacity: 1; }
.footer-base { display: flex; flex-wrap: wrap; justify-content: space-between; gap: var(--hig-space-sm); margin-top: var(--hig-space-section); color: var(--color-fd-muted-foreground); font-size: var(--text-xs); }
Expand All @@ -344,13 +344,13 @@ button[data-search-full] kbd {
}
@media (max-width: 480px) { .footer-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.docs-sidebar-links { display: flex; align-items: center; gap: var(--hig-space-lg); padding-top: var(--hig-space-lg); font-size: var(--text-xs); color: var(--color-fd-muted-foreground); }
.docs-sidebar-links a { transition: color var(--hig-duration-fast) var(--ease-spring); }
.docs-sidebar-links a { transition: color var(--hig-motion-duration-fast) var(--hig-motion-easing-standard); }
.docs-sidebar-links a:hover { color: var(--color-fd-foreground); }
.not-found { max-width: 37.5rem; padding: 7.5rem var(--hig-space-xxl); margin: 0 auto; }
.not-found h1 { font-size: var(--text-4xl); margin: var(--hig-space-xxl) 0; }
.not-found .hig-button { margin-top: var(--hig-space-xxxl); }
.not-found-links { display: flex; flex-wrap: wrap; gap: var(--hig-space-sm) var(--hig-space-xl); margin-top: var(--hig-space-xxxl); font: var(--text-2xs) / 1.5 var(--font-mono); letter-spacing: .09em; }
.not-found-links a { color: var(--color-fd-muted-foreground); transition: color var(--hig-duration-fast) var(--ease-spring); }
.not-found-links a { color: var(--color-fd-muted-foreground); transition: color var(--hig-motion-duration-fast) var(--hig-motion-easing-standard); }
.not-found-links a:hover { color: var(--color-fd-foreground); }
@media (max-width: 800px) {
.home { padding-top: var(--hig-space-xxxl); }
Expand All @@ -371,11 +371,11 @@ button[data-search-full] kbd {
}

/* HIG type roles and adaptive targets; the dense chart keeps compact controls. */
body { font-size: var(--hig-type-body-size); line-height: var(--hig-type-body-line); }
body { font-size: var(--hig-type-body-size); line-height: var(--hig-type-body-line-height); }
.hero h1 { font-size: clamp(var(--hig-type-title-size), 4.4vw, var(--hig-type-display-size)); line-height: 1.0833; font-weight: var(--hig-type-display-weight); }
#nd-page h1 { font-size: var(--hig-type-title-size); line-height: var(--hig-type-title-line); font-weight: var(--hig-type-title-weight); }
#nd-page h1 { font-size: var(--hig-type-title-size); line-height: var(--hig-type-title-line-height); font-weight: var(--hig-type-title-weight); }
@media (pointer: coarse) {
.docs-app-link, #nd-page .edit-on-github { min-height: var(--hig-control-comfortable); }
.docs-app-link, #nd-page .edit-on-github { min-height: var(--hig-density-comfortable-control); }
}
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after { animation-duration: 0ms !important; transition-duration: 0ms !important; scroll-behavior: auto !important; }
Expand All @@ -396,9 +396,6 @@ body { font-size: var(--hig-type-body-size); line-height: var(--hig-type-body-li
/* HIG Callout for MDX <Callout>; the body keeps prose typography. */
.docs-callout { margin-block: var(--hig-space-xxl); }
.docs-callout > .hig-surface { display: flex; flex-direction: column; gap: var(--hig-space-xs); padding: var(--hig-space-lg) var(--hig-space-xl); }
/* HIG marks warning/error severities; give info/success the same edge in their roles. */
.docs-callout > [data-severity='info'] { border-inline-start: 3px solid var(--hig-info); }
.docs-callout > [data-severity='success'] { border-inline-start: 3px solid var(--hig-positive); }
.docs-callout-body > :first-child { margin-top: 0; }
.docs-callout-body > :last-child { margin-bottom: 0; }

Expand Down
8 changes: 4 additions & 4 deletions apps/docs/components/callout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ import type { ReactNode } from 'react';
import { Callout as HigCallout } from '@bloxwap/hig-react/status';

type FumadocsType = 'info' | 'note' | 'idea' | 'success' | 'warn' | 'warning' | 'error';
const severity = { info: 'info', note: 'info', idea: 'info', success: 'success', warn: 'warning', warning: 'warning', error: 'error' } as const;
const fallbackTitle = { info: 'Note', success: 'Success', warning: 'Warning', error: 'Error' } as const;
const severity = { info: 'info', note: 'note', idea: 'tip', success: 'success', warn: 'warning', warning: 'warning', error: 'error' } as const;
const fallbackTitle = { info: 'Note', note: 'Note', tip: 'Tip', success: 'Success', warning: 'Warning', error: 'Error' } as const;

/** MDX `<Callout>` keeps the Fumadocs props and renders the HIG callout. Static content is not announced. */
/** MDX `<Callout>` keeps the Fumadocs props and renders the HIG callout (ReactNode titles, note/tip edges). Static content is not announced. */
export function Callout({ type = 'info', title, children }: { type?: FumadocsType; title?: ReactNode; children?: ReactNode }) {
const level = severity[type] ?? 'info';
return <div className="docs-callout">
<HigCallout title={typeof title === 'string' ? title : fallbackTitle[level]} severity={level} announce={false}>
<HigCallout title={title ?? fallbackTitle[level]} severity={level} announce={false}>
<div className="docs-callout-body">{children}</div>
</HigCallout>
</div>;
Expand Down
Loading
Loading