@setsquare/ui 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.2 — 2026-10-05
|
|
4
|
+
|
|
5
|
+
- **`AppHeader` takes a `brand`** for phones, where the breadcrumbs give way to the product. It showed the
|
|
6
|
+
shell's `productName` as plain text, so a product whose sidebar header shows a mark beside its name lost the mark
|
|
7
|
+
as soon as the sidebar became a sheet. Without `brand` the header is unchanged. Found by the Docuccino portal.
|
|
8
|
+
|
|
9
|
+
## 0.2.1 — 2026-10-04
|
|
10
|
+
|
|
11
|
+
- **The prefetch template imports nothing from Next's internals.** `templates/next/prefetch-on-intent.ts` imported
|
|
12
|
+
`PrefetchKind` from `next/dist/…/router-reducer-types`, a path Next doesn't promise to keep. It now passes
|
|
13
|
+
`{ kind: 'full' }` typed for itself; `package:verify` builds it and checks the page transition still runs.
|
|
14
|
+
- **Self-hosted fonts.** The README shows how to load Atkinson Hyperlegible Next and Mono from Fontsource with
|
|
15
|
+
`next/font/local`, for builds with no network and a CSP at `font-src 'self'`.
|
|
16
|
+
|
|
3
17
|
## 0.2.0 — 2026-10-04
|
|
4
18
|
|
|
5
19
|
Fixes and additions from the first product built on the package, the Docuccino portal.
|
package/README.md
CHANGED
|
@@ -29,7 +29,42 @@ In the app's global stylesheet, after Tailwind:
|
|
|
29
29
|
|
|
30
30
|
`styles.css` brings the tokens as CSS variables (four themes), Tailwind's theme mapping (`bg-primary`, `h-control`, `min-h-target`…), the `focus-ring` and type utilities, the `dark:` and `aaa:` variants, and an `@source` that points Tailwind at the package's compiled components, which Tailwind doesn't scan in `node_modules` by itself.
|
|
31
31
|
|
|
32
|
-
Load the fonts yourself: the tokens name Atkinson Hyperlegible Next and Atkinson Hyperlegible Mono
|
|
32
|
+
Load the fonts yourself: the tokens name Atkinson Hyperlegible Next and Atkinson Hyperlegible Mono. `next/font/google` downloads them at build time and serves them from your own origin; or set `--font-sans` and `--font-mono` to your own.
|
|
33
|
+
|
|
34
|
+
To serve them from the app with no Google request at build time either (a build with no network, a Content-Security-Policy at `font-src 'self'`), install the variable fonts from Fontsource (`@fontsource-variable/atkinson-hyperlegible-next` and `-mono`, SIL OFL 1.1) and load them with `next/font/local`:
|
|
35
|
+
|
|
36
|
+
```tsx
|
|
37
|
+
// app/layout.tsx
|
|
38
|
+
import localFont from 'next/font/local';
|
|
39
|
+
|
|
40
|
+
const sans = localFont({
|
|
41
|
+
src: [
|
|
42
|
+
{ path: '../node_modules/@fontsource-variable/atkinson-hyperlegible-next/files/atkinson-hyperlegible-next-latin-wght-normal.woff2', weight: '200 800', style: 'normal' },
|
|
43
|
+
{ path: '../node_modules/@fontsource-variable/atkinson-hyperlegible-next/files/atkinson-hyperlegible-next-latin-wght-italic.woff2', weight: '200 800', style: 'italic' },
|
|
44
|
+
],
|
|
45
|
+
variable: '--font-atkinson-next',
|
|
46
|
+
display: 'swap',
|
|
47
|
+
});
|
|
48
|
+
const mono = localFont({
|
|
49
|
+
src: [
|
|
50
|
+
{ path: '../node_modules/@fontsource-variable/atkinson-hyperlegible-mono/files/atkinson-hyperlegible-mono-latin-wght-normal.woff2', weight: '200 800', style: 'normal' },
|
|
51
|
+
],
|
|
52
|
+
variable: '--font-atkinson-mono',
|
|
53
|
+
display: 'swap',
|
|
54
|
+
});
|
|
55
|
+
// <html className={`${sans.variable} ${mono.variable}`}>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
```css
|
|
59
|
+
/* globals.css, after @setsquare/ui/styles.css. The fallback inside var() keeps the stack valid on a page
|
|
60
|
+
that doesn't load the fonts (a global-error page). */
|
|
61
|
+
:root {
|
|
62
|
+
--font-sans: var(--font-atkinson-next, 'Atkinson Hyperlegible Next'), ui-sans-serif, system-ui, sans-serif;
|
|
63
|
+
--font-mono: var(--font-atkinson-mono, 'Atkinson Hyperlegible Mono'), ui-monospace, monospace;
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
`next/font` gives the face a generated family name, which is why the stack points at its variable rather than at the name the tokens use. Add the `latin-ext` files too if your copy needs them.
|
|
33
68
|
|
|
34
69
|
```tsx
|
|
35
70
|
import { Button, DashboardShell, ShellMain, TextField } from '@setsquare/ui';
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
2
|
import { type SidebarTriggerLabels } from './sidebar.js';
|
|
3
3
|
export interface AppHeaderProps {
|
|
4
|
-
/** Usually <Breadcrumbs>. On phones
|
|
4
|
+
/** Usually <Breadcrumbs>. On phones `brand` shows instead. */
|
|
5
5
|
breadcrumbs?: ReactNode;
|
|
6
|
+
/**
|
|
7
|
+
* What shows on phones in place of the breadcrumbs: the product's mark and name, as its sidebar header shows
|
|
8
|
+
* them, so the brand doesn't change when the sidebar becomes a sheet. Not a link: a second link to the home page
|
|
9
|
+
* under another name fails 3.2.4. Default: the shell's `productName`, in bold.
|
|
10
|
+
*/
|
|
11
|
+
brand?: ReactNode;
|
|
6
12
|
/** Usually <AppSearch>. */
|
|
7
13
|
search?: ReactNode;
|
|
8
14
|
/** Help, notifications — the same buttons in the same order on every page (help: 3.2.6; notifications: our rule). */
|
|
@@ -12,5 +18,5 @@ export interface AppHeaderProps {
|
|
|
12
18
|
className?: string;
|
|
13
19
|
}
|
|
14
20
|
/** The inset's top bar: sidebar trigger, where you are, search and the fixed actions. */
|
|
15
|
-
export declare function AppHeader({ breadcrumbs, search, actions, labels, className }: AppHeaderProps): import("react").JSX.Element;
|
|
21
|
+
export declare function AppHeader({ breadcrumbs, brand, search, actions, labels, className, }: AppHeaderProps): import("react").JSX.Element;
|
|
16
22
|
//# sourceMappingURL=app-header.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-header.d.ts","sourceRoot":"","sources":["../../src/shell/app-header.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,OAAO,EAAkB,KAAK,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAEzE,MAAM,WAAW,cAAc;IAC7B,
|
|
1
|
+
{"version":3,"file":"app-header.d.ts","sourceRoot":"","sources":["../../src/shell/app-header.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,OAAO,EAAkB,KAAK,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAEzE,MAAM,WAAW,cAAc;IAC7B,8DAA8D;IAC9D,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB;;;;OAIG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,2BAA2B;IAC3B,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,qHAAqH;IACrH,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,0CAA0C;IAC1C,MAAM,CAAC,EAAE,OAAO,CAAC,oBAAoB,CAAC,CAAC;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,yFAAyF;AACzF,wBAAgB,SAAS,CAAC,EACxB,WAAW,EACX,KAAK,EACL,MAAM,EACN,OAAO,EACP,MAAM,EACN,SAAS,GACV,EAAE,cAAc,+BAuBhB"}
|
package/dist/shell/app-header.js
CHANGED
|
@@ -4,8 +4,10 @@ import { cn } from '../lib/cn.js';
|
|
|
4
4
|
import { useShell } from './dashboard-shell.js';
|
|
5
5
|
import { SidebarTrigger } from './sidebar.js';
|
|
6
6
|
/** The inset's top bar: sidebar trigger, where you are, search and the fixed actions. */
|
|
7
|
-
export function AppHeader({ breadcrumbs, search, actions, labels, className }) {
|
|
7
|
+
export function AppHeader({ breadcrumbs, brand, search, actions, labels, className, }) {
|
|
8
8
|
const { size, productName } = useShell();
|
|
9
|
-
return (_jsxs("header", { style: { viewTransitionName: 'shell-header' }, className: cn('flex h-header shrink-0 items-center gap-2 border-b border-border px-3 @min-[48rem]/shell:px-4', className), children: [_jsx(SidebarTrigger, { labels: labels }), _jsx("span", { "aria-hidden": "true", className: "mx-1 h-6 w-px bg-border @max-[48rem]/shell:hidden" }), _jsx("div", { className: "flex min-w-0 flex-1 items-center", children: size === 'mobile'
|
|
9
|
+
return (_jsxs("header", { style: { viewTransitionName: 'shell-header' }, className: cn('flex h-header shrink-0 items-center gap-2 border-b border-border px-3 @min-[48rem]/shell:px-4', className), children: [_jsx(SidebarTrigger, { labels: labels }), _jsx("span", { "aria-hidden": "true", className: "mx-1 h-6 w-px bg-border @max-[48rem]/shell:hidden" }), _jsx("div", { className: "flex min-w-0 flex-1 items-center", children: size === 'mobile'
|
|
10
|
+
? (brand ?? _jsx("span", { className: "truncate text-base font-bold", children: productName }))
|
|
11
|
+
: breadcrumbs }), _jsxs("div", { className: "flex shrink-0 items-center gap-1", children: [search, actions] })] }));
|
|
10
12
|
}
|
|
11
13
|
//# sourceMappingURL=app-header.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-header.js","sourceRoot":"","sources":["../../src/shell/app-header.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAGb,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAClC,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChD,OAAO,EAAE,cAAc,EAA6B,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"app-header.js","sourceRoot":"","sources":["../../src/shell/app-header.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAGb,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAClC,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChD,OAAO,EAAE,cAAc,EAA6B,MAAM,cAAc,CAAC;AAoBzE,yFAAyF;AACzF,MAAM,UAAU,SAAS,CAAC,EACxB,WAAW,EACX,KAAK,EACL,MAAM,EACN,OAAO,EACP,MAAM,EACN,SAAS,GACM;IACf,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,GAAG,QAAQ,EAAE,CAAC;IACzC,OAAO,CACL,kBACE,KAAK,EAAE,EAAE,kBAAkB,EAAE,cAAc,EAAE,EAC7C,SAAS,EAAE,EAAE,CACX,+FAA+F,EAC/F,SAAS,CACV,aAED,KAAC,cAAc,IAAC,MAAM,EAAE,MAAM,GAAI,EAClC,8BAAkB,MAAM,EAAC,SAAS,EAAC,mDAAmD,GAAG,EACzF,cAAK,SAAS,EAAC,kCAAkC,YAC9C,IAAI,KAAK,QAAQ;oBAChB,CAAC,CAAC,CAAC,KAAK,IAAI,eAAM,SAAS,EAAC,8BAA8B,YAAE,WAAW,GAAQ,CAAC;oBAChF,CAAC,CAAC,WAAW,GACX,EACN,eAAK,SAAS,EAAC,kCAAkC,aAC9C,MAAM,EACN,OAAO,IACJ,IACC,CACV,CAAC;AACJ,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@setsquare/ui",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "React Aria components, design tokens, a dashboard shell and a site shell, with a switch between WCAG 2.2 AA and AAA sizing, spacing and contrast.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://github.com/Worktree-Amigos/setsquare-ui#readme",
|
|
@@ -3,8 +3,15 @@
|
|
|
3
3
|
|
|
4
4
|
import { useEffect } from 'react';
|
|
5
5
|
import type { useRouter } from 'next/navigation';
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* `router.prefetch` as Next 16 runs it. It reads `options.kind`, and `'full'` is the value of its internal
|
|
9
|
+
* `PrefetchKind.FULL` (the whole page, which a dynamic route needs), but its public types and documentation list
|
|
10
|
+
* only `onInvalidate`, and typed routes drop the options altogether. Typed here rather than imported from
|
|
11
|
+
* `next/dist`, so the template depends on no internal module path; if Next stops reading `kind`, the prefetch
|
|
12
|
+
* falls back to its default.
|
|
13
|
+
*/
|
|
14
|
+
type FullPrefetch = (href: string, options: { kind: 'full' }) => void;
|
|
8
15
|
|
|
9
16
|
/**
|
|
10
17
|
* Warms a page when a pointer moves over a link to it or the link takes focus, as Next's own <Link> does —
|
|
@@ -19,7 +26,7 @@ export function usePrefetchOnIntent(router: ReturnType<typeof useRouter>) {
|
|
|
19
26
|
const link = e.target instanceof Element ? e.target.closest('a[href^="/"]') : null;
|
|
20
27
|
const href = link?.getAttribute('href')?.replace(/#.*$/, '');
|
|
21
28
|
if (href && href !== window.location.pathname)
|
|
22
|
-
router.prefetch(href, { kind:
|
|
29
|
+
(router.prefetch as unknown as FullPrefetch).call(router, href, { kind: 'full' });
|
|
23
30
|
};
|
|
24
31
|
document.addEventListener('pointerover', warm);
|
|
25
32
|
document.addEventListener('focusin', warm);
|