@bitbaum/sitekit 0.6.1 → 0.7.0
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/README.md +11 -0
- package/dist/react/SiteChrome.d.ts +8 -1
- package/dist/react/SiteChrome.js +2 -2
- package/dist/react/icons.d.ts +19 -0
- package/dist/react/icons.js +26 -0
- package/dist/react/index.d.ts +1 -0
- package/dist/react/index.js +1 -0
- package/package.json +1 -1
- package/styles.css +63 -15
package/README.md
CHANGED
|
@@ -183,3 +183,14 @@ can configure `--sk-header-height`, `--sk-mobile-header-height`,
|
|
|
183
183
|
`SiteTabBar` renders a fixed bottom navigation for small screens (hidden from
|
|
184
184
|
48rem up, where the masthead nav takes over). Pass 3–5 `{ path, label, icon }`
|
|
185
185
|
items and the current path; it renders its own spacer so content is not hidden.
|
|
186
|
+
|
|
187
|
+
Use `SiteIcon` for the icons — `{ icon: <SiteIcon name="write" /> }` — not a
|
|
188
|
+
unicode glyph: the set is drawn on `currentColor`, so icon and label take one
|
|
189
|
+
colour and the current tab is one rule. Names: `SITE_ICON_NAMES`. Pass
|
|
190
|
+
`tabBar` to `SiteMasthead` on the same page so the masthead drops its menu
|
|
191
|
+
button on phones instead of repeating the tab bar's destinations:
|
|
192
|
+
|
|
193
|
+
```tsx
|
|
194
|
+
<SiteMasthead appearance="application" tabBar navItems={nav} … />
|
|
195
|
+
<SiteTabBar items={nav.map((n) => ({ ...n, icon: <SiteIcon name={n.icon} /> }))} … />
|
|
196
|
+
```
|
|
@@ -18,8 +18,15 @@ interface Props {
|
|
|
18
18
|
skipLabel?: string;
|
|
19
19
|
navLabel?: string;
|
|
20
20
|
menuLabel?: string;
|
|
21
|
+
/**
|
|
22
|
+
* The page also renders a `SiteTabBar` with these destinations. On phones
|
|
23
|
+
* the tab bar is then the navigation, and the masthead's menu button is
|
|
24
|
+
* dropped below the tab bar breakpoint instead of repeating it — one way to
|
|
25
|
+
* each place, not two. The account actions stay in the masthead.
|
|
26
|
+
*/
|
|
27
|
+
tabBar?: boolean;
|
|
21
28
|
}
|
|
22
|
-
export declare function SiteMasthead({ chrome, navItems, navGroups, currentPath, Link, appearance, brand, actions, mainId, skipLabel, navLabel, menuLabel, }: Props): import("react").JSX.Element;
|
|
29
|
+
export declare function SiteMasthead({ chrome, navItems, navGroups, currentPath, Link, appearance, brand, actions, mainId, skipLabel, navLabel, menuLabel, tabBar, }: Props): import("react").JSX.Element;
|
|
23
30
|
export declare function SiteFooter({ chrome, appearance, links, groups, Link, label, extra, }: {
|
|
24
31
|
chrome: SiteChromeSpec;
|
|
25
32
|
appearance?: 'application';
|
package/dist/react/SiteChrome.js
CHANGED
|
@@ -13,9 +13,9 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
13
13
|
import { href } from '../model.js';
|
|
14
14
|
import { DefaultLink } from './link.js';
|
|
15
15
|
import { SiteNav } from './SiteNav.js';
|
|
16
|
-
export function SiteMasthead({ chrome, navItems, navGroups, currentPath, Link = DefaultLink, appearance, brand, actions, mainId, skipLabel = 'Skip to content', navLabel = 'Main navigation', menuLabel = 'Menu', }) {
|
|
16
|
+
export function SiteMasthead({ chrome, navItems, navGroups, currentPath, Link = DefaultLink, appearance, brand, actions, mainId, skipLabel = 'Skip to content', navLabel = 'Main navigation', menuLabel = 'Menu', tabBar = false, }) {
|
|
17
17
|
if (appearance === 'application')
|
|
18
|
-
return (_jsxs(_Fragment, { children: [mainId && (_jsx("a", { className: "sk-skip", href: '#' + mainId, children: skipLabel })), _jsx("header", { className:
|
|
18
|
+
return (_jsxs(_Fragment, { children: [mainId && (_jsx("a", { className: "sk-skip", href: '#' + mainId, children: skipLabel })), _jsx("header", { className: tabBar ? 'sk-masthead sk-masthead--tabbar' : 'sk-masthead', children: _jsxs("div", { className: "sk-shell-frame sk-masthead-row", children: [_jsx(Link, { href: "/", className: "sk-brand", children: brand ?? chrome.name }), _jsx(SiteNav, { items: navItems, groups: navGroups, currentPath: currentPath, Link: Link, appearance: "application", label: navLabel, menuLabel: menuLabel, menuActions: actions }), actions && _jsx("div", { className: "sk-account-actions", children: actions })] }) })] }));
|
|
19
19
|
return (_jsx("header", { className: "sticky top-0 z-30 border-b border-subtle bg-surface-page/85 backdrop-blur", children: _jsx("div", { className: "mx-auto max-w-shell px-4 sm:px-6 lg:px-8", children: _jsxs("div", { className: "flex flex-wrap items-center justify-between gap-x-6 py-2", children: [_jsx(Link, { href: href(), className: "inline-flex min-h-11 shrink-0 items-center rounded focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent", children: _jsx("span", { className: "font-heading text-lg font-semibold tracking-display text-fg-primary", children: chrome.name }) }), _jsx(SiteNav, { items: navItems, currentPath: currentPath, Link: Link })] }) }) }));
|
|
20
20
|
}
|
|
21
21
|
export function SiteFooter({ chrome, appearance, links = [], groups, Link = DefaultLink, label = 'Footer navigation', extra, }) {
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One small icon set for the shell: the tab bar and the account actions.
|
|
3
|
+
*
|
|
4
|
+
* Products used to pass unicode glyphs (◎ ✎ ⌕ ⇄ ★) as tab icons, which
|
|
5
|
+
* render differently on every phone and never look like an interface. These
|
|
6
|
+
* are 24-unit stroke outlines on `currentColor`, so a tab's icon and label
|
|
7
|
+
* take the same colour and the active state is one rule. Decorative only:
|
|
8
|
+
* every one is `aria-hidden`, the label beside it is what is read out.
|
|
9
|
+
*/
|
|
10
|
+
import type { SVGProps } from 'react';
|
|
11
|
+
export type SiteIconName = 'home' | 'write' | 'search' | 'compass' | 'star' | 'user' | 'chat' | 'bell' | 'settings' | 'list' | 'globe' | 'book' | 'heart' | 'plus' | 'menu' | 'calendar' | 'wallet' | 'chart';
|
|
12
|
+
export interface SiteIconProps extends Omit<SVGProps<SVGSVGElement>, 'name'> {
|
|
13
|
+
name: SiteIconName;
|
|
14
|
+
/** Rendered size in CSS pixels; the tab bar sets its own. */
|
|
15
|
+
size?: number | string;
|
|
16
|
+
}
|
|
17
|
+
/** A shell icon on `currentColor`. Decorative: hidden from assistive tech. */
|
|
18
|
+
export declare function SiteIcon({ name, size, className, ...rest }: SiteIconProps): import("react").JSX.Element;
|
|
19
|
+
export declare const SITE_ICON_NAMES: SiteIconName[];
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
const PATHS = {
|
|
3
|
+
home: (_jsxs(_Fragment, { children: [_jsx("path", { d: "M3 11.5 12 4l9 7.5" }), _jsx("path", { d: "M5.5 10.5V20h13v-9.5" }), _jsx("path", { d: "M10 20v-5h4v5" })] })),
|
|
4
|
+
write: (_jsxs(_Fragment, { children: [_jsx("path", { d: "M4 20h4l10.5-10.5a2.1 2.1 0 0 0-3-3L5 17z" }), _jsx("path", { d: "M13.5 8.5 16.5 11.5" })] })),
|
|
5
|
+
search: (_jsxs(_Fragment, { children: [_jsx("circle", { cx: "10.5", cy: "10.5", r: "6.5" }), _jsx("path", { d: "M20 20l-4.5-4.5" })] })),
|
|
6
|
+
compass: (_jsxs(_Fragment, { children: [_jsx("circle", { cx: "12", cy: "12", r: "9" }), _jsx("path", { d: "m15.5 8.5-2 5-5 2 2-5z" })] })),
|
|
7
|
+
star: _jsx("path", { d: "m12 3.5 2.6 5.4 5.9.8-4.3 4.1 1.1 5.9L12 16.9l-5.3 2.8 1.1-5.9-4.3-4.1 5.9-.8z" }),
|
|
8
|
+
user: (_jsxs(_Fragment, { children: [_jsx("circle", { cx: "12", cy: "8.5", r: "4" }), _jsx("path", { d: "M4.5 20.5c.8-4 4-6 7.5-6s6.7 2 7.5 6" })] })),
|
|
9
|
+
chat: _jsx("path", { d: "M4 5.5h16v10H9.5L5.5 19v-3.5H4z" }),
|
|
10
|
+
bell: (_jsxs(_Fragment, { children: [_jsx("path", { d: "M6 16.5V11a6 6 0 0 1 12 0v5.5l1.5 2h-15z" }), _jsx("path", { d: "M10 20.5a2 2 0 0 0 4 0" })] })),
|
|
11
|
+
settings: (_jsxs(_Fragment, { children: [_jsx("circle", { cx: "12", cy: "12", r: "3" }), _jsx("path", { d: "M12 3v2.5M12 18.5V21M3 12h2.5M18.5 12H21M5.6 5.6l1.8 1.8M16.6 16.6l1.8 1.8M5.6 18.4l1.8-1.8M16.6 7.4l1.8-1.8" })] })),
|
|
12
|
+
list: _jsx("path", { d: "M8 6h12M8 12h12M8 18h12M4 6h.01M4 12h.01M4 18h.01" }),
|
|
13
|
+
globe: (_jsxs(_Fragment, { children: [_jsx("circle", { cx: "12", cy: "12", r: "9" }), _jsx("path", { d: "M3 12h18M12 3c3 3.2 3 14.8 0 18M12 3c-3 3.2-3 14.8 0 18" })] })),
|
|
14
|
+
book: (_jsxs(_Fragment, { children: [_jsx("path", { d: "M4 5.5A2.5 2.5 0 0 1 6.5 3H20v15H6.5A2.5 2.5 0 0 0 4 20.5z" }), _jsx("path", { d: "M4 18A2.5 2.5 0 0 1 6.5 15.5H20" })] })),
|
|
15
|
+
heart: (_jsx("path", { d: "M12 20s-7.5-4.6-7.5-10A4.2 4.2 0 0 1 12 7.7 4.2 4.2 0 0 1 19.5 10c0 5.4-7.5 10-7.5 10z" })),
|
|
16
|
+
plus: _jsx("path", { d: "M12 5v14M5 12h14" }),
|
|
17
|
+
menu: _jsx("path", { d: "M4 7h16M4 12h16M4 17h16" }),
|
|
18
|
+
calendar: (_jsxs(_Fragment, { children: [_jsx("rect", { x: "4", y: "5", width: "16", height: "15", rx: "2" }), _jsx("path", { d: "M4 10h16M8 3v4M16 3v4" })] })),
|
|
19
|
+
wallet: (_jsxs(_Fragment, { children: [_jsx("path", { d: "M3 7.5h16a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" }), _jsx("path", { d: "M3 7.5V6a2 2 0 0 1 2-2h11" }), _jsx("circle", { cx: "16.5", cy: "14", r: "1.2" })] })),
|
|
20
|
+
chart: _jsx("path", { d: "M4 20V4M4 20h16M8 16v-5M12 16V8M16 16v-3M20 16V6" }),
|
|
21
|
+
};
|
|
22
|
+
/** A shell icon on `currentColor`. Decorative: hidden from assistive tech. */
|
|
23
|
+
export function SiteIcon({ name, size, className, ...rest }) {
|
|
24
|
+
return (_jsx("svg", { viewBox: "0 0 24 24", width: size, height: size, fill: "none", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", focusable: "false", className: className ? `sk-icon ${className}` : 'sk-icon', "data-icon": name, ...rest, children: PATHS[name] }));
|
|
25
|
+
}
|
|
26
|
+
export const SITE_ICON_NAMES = Object.keys(PATHS);
|
package/dist/react/index.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export { SiteSections } from './SiteSections.js';
|
|
|
10
10
|
export { SiteMasthead, SiteFooter } from './SiteChrome.js';
|
|
11
11
|
export { SiteNav, type SiteNavGroup } from './SiteNav.js';
|
|
12
12
|
export { SiteTabBar, type SiteTabItem } from './SiteTabBar.js';
|
|
13
|
+
export { SiteIcon, SITE_ICON_NAMES, type SiteIconName, type SiteIconProps } from './icons.js';
|
|
13
14
|
export { NavAutoScroll } from './NavAutoScroll.js';
|
|
14
15
|
export { DefaultLink, type LinkLike, type LinkProps } from './link.js';
|
|
15
16
|
export { SectionHeading, Blurb, SectionBody } from './sections/Primitives.js';
|
package/dist/react/index.js
CHANGED
|
@@ -10,6 +10,7 @@ export { SiteSections } from './SiteSections.js';
|
|
|
10
10
|
export { SiteMasthead, SiteFooter } from './SiteChrome.js';
|
|
11
11
|
export { SiteNav } from './SiteNav.js';
|
|
12
12
|
export { SiteTabBar } from './SiteTabBar.js';
|
|
13
|
+
export { SiteIcon, SITE_ICON_NAMES } from './icons.js';
|
|
13
14
|
export { NavAutoScroll } from './NavAutoScroll.js';
|
|
14
15
|
export { DefaultLink } from './link.js';
|
|
15
16
|
export { SectionHeading, Blurb, SectionBody } from './sections/Primitives.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bitbaum/sitekit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "A website as data: a closed union of section shapes validated at runtime, one set of React renderers, and per-field provenance so a generated site can prove it fabricated nothing.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Mao Nakamoto",
|
package/styles.css
CHANGED
|
@@ -339,37 +339,51 @@
|
|
|
339
339
|
gap: 1.5rem;
|
|
340
340
|
}
|
|
341
341
|
}
|
|
342
|
-
/* Mobile bottom tab bar (SiteTabBar). Hidden from --sk-tabbar-breakpoint up.
|
|
342
|
+
/* Mobile bottom tab bar (SiteTabBar). Hidden from --sk-tabbar-breakpoint up.
|
|
343
|
+
One row of equal cells; the current one carries a short rule above its icon
|
|
344
|
+
and the full text colour, so the active state reads without a fill. */
|
|
343
345
|
.sk-tabbar {
|
|
344
346
|
position: fixed;
|
|
345
347
|
inset: auto 0 0 0;
|
|
346
348
|
z-index: 45;
|
|
347
|
-
display:
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
padding:
|
|
349
|
+
display: grid;
|
|
350
|
+
grid-auto-flow: column;
|
|
351
|
+
grid-auto-columns: minmax(0, 1fr);
|
|
352
|
+
padding: 0 4px calc(0px + env(safe-area-inset-bottom));
|
|
351
353
|
background: var(--sk-bg, Canvas);
|
|
352
354
|
border-top: 1px solid var(--sk-line, #d6d6d6);
|
|
353
355
|
font-family: var(--sk-font, system-ui, sans-serif);
|
|
354
356
|
}
|
|
355
357
|
.sk-tab {
|
|
356
|
-
|
|
358
|
+
position: relative;
|
|
357
359
|
min-width: 0;
|
|
360
|
+
min-height: 56px;
|
|
358
361
|
display: flex;
|
|
359
362
|
flex-direction: column;
|
|
360
363
|
align-items: center;
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
+
justify-content: center;
|
|
365
|
+
gap: 3px;
|
|
366
|
+
padding: 8px 4px 6px;
|
|
364
367
|
color: var(--sk-muted, #555);
|
|
365
368
|
font-size: 11px;
|
|
366
369
|
font-weight: 600;
|
|
370
|
+
letter-spacing: 0.01em;
|
|
367
371
|
text-decoration: none;
|
|
372
|
+
-webkit-tap-highlight-color: transparent;
|
|
368
373
|
}
|
|
369
374
|
.sk-tab-icon {
|
|
370
|
-
|
|
375
|
+
display: inline-flex;
|
|
376
|
+
width: 24px;
|
|
377
|
+
height: 24px;
|
|
378
|
+
align-items: center;
|
|
379
|
+
justify-content: center;
|
|
380
|
+
font-size: 22px;
|
|
371
381
|
line-height: 1;
|
|
372
382
|
}
|
|
383
|
+
.sk-tab-icon > svg {
|
|
384
|
+
width: 24px;
|
|
385
|
+
height: 24px;
|
|
386
|
+
}
|
|
373
387
|
.sk-tab-label {
|
|
374
388
|
white-space: nowrap;
|
|
375
389
|
overflow: hidden;
|
|
@@ -378,14 +392,36 @@
|
|
|
378
392
|
}
|
|
379
393
|
.sk-tab[aria-current='page'] {
|
|
380
394
|
color: var(--sk-fg, CanvasText);
|
|
381
|
-
|
|
395
|
+
}
|
|
396
|
+
.sk-tab[aria-current='page']::before {
|
|
397
|
+
content: '';
|
|
398
|
+
position: absolute;
|
|
399
|
+
top: -1px;
|
|
400
|
+
left: 50%;
|
|
401
|
+
width: 28px;
|
|
402
|
+
height: 3px;
|
|
403
|
+
transform: translateX(-50%);
|
|
404
|
+
border-radius: 0 0 3px 3px;
|
|
405
|
+
background: var(--sk-tab-active, currentColor);
|
|
382
406
|
}
|
|
383
407
|
.sk-tab:focus-visible {
|
|
384
408
|
outline: 2px solid var(--sk-focus, Highlight);
|
|
385
|
-
outline-offset:
|
|
409
|
+
outline-offset: -4px;
|
|
410
|
+
border-radius: 10px;
|
|
386
411
|
}
|
|
387
412
|
.sk-tabbar-spacer {
|
|
388
|
-
height: var(--sk-tabbar-height,
|
|
413
|
+
height: calc(var(--sk-tabbar-height, 56px) + env(safe-area-inset-bottom));
|
|
414
|
+
}
|
|
415
|
+
/* Below the tab bar breakpoint the tab bar IS the navigation: a masthead that
|
|
416
|
+
says so (`tabBar`) drops its menu button rather than offering the same five
|
|
417
|
+
places twice. */
|
|
418
|
+
@media (max-width: 47.999rem) {
|
|
419
|
+
.sk-masthead--tabbar .sk-mobile-menu {
|
|
420
|
+
display: none;
|
|
421
|
+
}
|
|
422
|
+
.sk-masthead--tabbar .sk-masthead-row:has(.sk-mobile-menu) > .sk-account-actions {
|
|
423
|
+
display: flex;
|
|
424
|
+
}
|
|
389
425
|
}
|
|
390
426
|
@media (min-width: 48rem) {
|
|
391
427
|
.sk-tabbar,
|
|
@@ -393,8 +429,15 @@
|
|
|
393
429
|
display: none;
|
|
394
430
|
}
|
|
395
431
|
}
|
|
396
|
-
|
|
432
|
+
.sk-icon {
|
|
433
|
+
display: inline-block;
|
|
434
|
+
vertical-align: middle;
|
|
435
|
+
flex-shrink: 0;
|
|
436
|
+
}
|
|
397
437
|
/* One native disclosure and one link tree for grouped application navigation. */
|
|
438
|
+
.sk-masthead-row:has(.sk-grouped-menu) {
|
|
439
|
+
position: relative;
|
|
440
|
+
}
|
|
398
441
|
.sk-grouped-menu {
|
|
399
442
|
margin-inline-start: auto;
|
|
400
443
|
}
|
|
@@ -442,7 +485,7 @@
|
|
|
442
485
|
font-size: 0.875rem;
|
|
443
486
|
color: var(--sk-muted, #555);
|
|
444
487
|
}
|
|
445
|
-
.sk-grouped-actions a,
|
|
488
|
+
.sk-grouped-actions > a,
|
|
446
489
|
.sk-nav-group a {
|
|
447
490
|
display: flex;
|
|
448
491
|
flex-direction: column;
|
|
@@ -477,6 +520,11 @@
|
|
|
477
520
|
}
|
|
478
521
|
}
|
|
479
522
|
@media (max-width: 380px) {
|
|
523
|
+
/* Give host account popovers the full narrow viewport, including padding. */
|
|
524
|
+
.sk-grouped-panel {
|
|
525
|
+
inset-inline: 0;
|
|
526
|
+
width: 100%;
|
|
527
|
+
}
|
|
480
528
|
.sk-masthead-row:has(.sk-grouped-menu) > .sk-account-actions {
|
|
481
529
|
display: none;
|
|
482
530
|
}
|