shelving 1.264.2 → 1.266.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/package.json +1 -1
- package/ui/form/Progress.d.ts +9 -6
- package/ui/form/Progress.js +12 -6
- package/ui/form/Progress.md +62 -0
- package/ui/form/Progress.module.css +18 -21
- package/ui/form/Progress.tsx +19 -22
- package/ui/layout/CenteredLayout.d.ts +5 -4
- package/ui/layout/CenteredLayout.js +11 -3
- package/ui/layout/CenteredLayout.md +12 -10
- package/ui/layout/CenteredLayout.module.css +4 -2
- package/ui/layout/CenteredLayout.tsx +18 -7
- package/ui/layout/SidebarLayout.md +5 -4
- package/ui/layout/SidebarLayout.module.css +10 -6
package/package.json
CHANGED
package/ui/form/Progress.d.ts
CHANGED
|
@@ -1,24 +1,27 @@
|
|
|
1
1
|
import type { ReactElement } from "react";
|
|
2
|
+
import { type ColorVariants } from "../style/Color.js";
|
|
3
|
+
import { type StatusVariants } from "../style/Status.js";
|
|
2
4
|
/**
|
|
3
5
|
* Props for `Progress`, a continuous horizontal progress bar.
|
|
4
6
|
*
|
|
5
7
|
* @see https://shelving.cc/ui/ProgressProps
|
|
6
8
|
*/
|
|
7
|
-
export interface ProgressProps {
|
|
9
|
+
export interface ProgressProps extends ColorVariants, StatusVariants {
|
|
8
10
|
value: number;
|
|
9
11
|
min?: number;
|
|
10
12
|
max?: number;
|
|
11
|
-
success?: boolean;
|
|
12
|
-
warning?: boolean;
|
|
13
|
-
danger?: boolean;
|
|
14
13
|
}
|
|
15
14
|
/**
|
|
16
15
|
* Show progress as a single continuous horizontal bar, filled to `value` within the `min`–`max` range (matches `getPercent()` and `formatPercent()`).
|
|
17
|
-
* -
|
|
16
|
+
* - Renders a native `<progress>` element, so `role="progressbar"` and the value/max semantics come from the browser.
|
|
17
|
+
* - `<progress>` has no `min` attribute (its implicit minimum is `0`), so the range is normalised to `value - min` / `max - min` before it's handed to the element.
|
|
18
|
+
* - The browser clamps `value` to the `0`–`max` range, so an out-of-range `value` shows an empty or full bar rather than overspilling.
|
|
19
|
+
* - Paints from the tint ladder, so `color=` / `status=` recolour the fill (and track) by moving the tint anchor.
|
|
18
20
|
*
|
|
19
21
|
* @returns A progress bar element.
|
|
20
22
|
* @kind component
|
|
21
23
|
* @example <Progress value={3} max={4} />
|
|
24
|
+
* @example <Progress value={90} status="success" />
|
|
22
25
|
* @see https://shelving.cc/ui/Progress
|
|
23
26
|
*/
|
|
24
|
-
export declare function Progress({ value, min, max,
|
|
27
|
+
export declare function Progress({ value, min, max, ...props }: ProgressProps): ReactElement;
|
package/ui/form/Progress.js
CHANGED
|
@@ -1,19 +1,25 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { formatPercent } from "../../util/format.js";
|
|
3
|
-
import {
|
|
3
|
+
import { getColorClass } from "../style/Color.js";
|
|
4
|
+
import { getStatusClass } from "../style/Status.js";
|
|
4
5
|
import { getClass, getModuleClass } from "../util/css.js";
|
|
5
6
|
import styles from "./Progress.module.css";
|
|
6
7
|
/**
|
|
7
8
|
* Show progress as a single continuous horizontal bar, filled to `value` within the `min`–`max` range (matches `getPercent()` and `formatPercent()`).
|
|
8
|
-
* -
|
|
9
|
+
* - Renders a native `<progress>` element, so `role="progressbar"` and the value/max semantics come from the browser.
|
|
10
|
+
* - `<progress>` has no `min` attribute (its implicit minimum is `0`), so the range is normalised to `value - min` / `max - min` before it's handed to the element.
|
|
11
|
+
* - The browser clamps `value` to the `0`–`max` range, so an out-of-range `value` shows an empty or full bar rather than overspilling.
|
|
12
|
+
* - Paints from the tint ladder, so `color=` / `status=` recolour the fill (and track) by moving the tint anchor.
|
|
9
13
|
*
|
|
10
14
|
* @returns A progress bar element.
|
|
11
15
|
* @kind component
|
|
12
16
|
* @example <Progress value={3} max={4} />
|
|
17
|
+
* @example <Progress value={90} status="success" />
|
|
13
18
|
* @see https://shelving.cc/ui/Progress
|
|
14
19
|
*/
|
|
15
|
-
export function Progress({ value, min = 0, max = 100,
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
return (_jsx("
|
|
20
|
+
export function Progress({ value, min = 0, max = 100, ...props }) {
|
|
21
|
+
// `<progress>` has no `min`, so shift the range to a `0`-based one. A non-positive span would make `<progress>` indeterminate, so fall back to an empty determinate bar.
|
|
22
|
+
const span = max - min;
|
|
23
|
+
return (_jsx("progress", { className: getClass(getModuleClass(styles, "progress"), //
|
|
24
|
+
getColorClass(props), getStatusClass(props)), value: span > 0 ? value - min : 0, max: span > 0 ? span : 1, "aria-valuetext": formatPercent(value - min, max - min) }));
|
|
19
25
|
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Progress
|
|
2
|
+
|
|
3
|
+
A continuous horizontal progress bar for the completion of a task. Rendered as a native `<progress>` element, so the `progressbar` role and value/max semantics come from the browser rather than hand-written ARIA.
|
|
4
|
+
|
|
5
|
+
**Things to know:**
|
|
6
|
+
|
|
7
|
+
- Use `Progress` for **task completion** — a value heading toward "done" (an upload, a multi-step form, a load). For a static reading within a range that can move up and down (disk usage, a score), a gauge is the right model, not a progress bar.
|
|
8
|
+
- `value` is filled within the `min`–`max` range (defaults `0`–`100`), matching `getPercent()` and `formatPercent()`. `<progress>` has no `min` attribute, so the range is normalised to `value - min` / `max - min` before it reaches the element.
|
|
9
|
+
- The browser clamps `value` to the `0`–`max` range, so an out-of-range `value` renders an empty or full bar rather than overspilling. A non-positive range (`min === max`) falls back to an empty bar rather than the indeterminate state.
|
|
10
|
+
- `aria-valuetext` carries the formatted percentage, so assistive tech announces e.g. "75%".
|
|
11
|
+
- Paints from the [tint ladder](/ui/TINT_CLASS): `color=` and `status=` move the tint anchor for the bar, so the fill (and track) re-derive together — `status="success"` gives a green bar, `color="purple"` a purple one. Without either, the bar takes the ambient tint (`--tint-50`, gray by default).
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
### Basic
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
import { Progress } from "shelving/ui";
|
|
19
|
+
|
|
20
|
+
// 75% full.
|
|
21
|
+
<Progress value={3} max={4} />
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Custom range
|
|
25
|
+
|
|
26
|
+
```tsx
|
|
27
|
+
import { Progress } from "shelving/ui";
|
|
28
|
+
|
|
29
|
+
// 50% full — value 15 within the 10–20 range.
|
|
30
|
+
<Progress value={15} min={10} max={20} />
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Status and colour
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
import { Progress } from "shelving/ui";
|
|
37
|
+
|
|
38
|
+
<Progress value={90} status="success" />
|
|
39
|
+
<Progress value={20} status="danger" />
|
|
40
|
+
<Progress value={60} color="purple" />
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Styling
|
|
44
|
+
|
|
45
|
+
`Progress` paints a track (the element / `::-webkit-progress-bar`) and a fill (`::-webkit-progress-value` / `::-moz-progress-bar`) from the [tint ladder](/ui/TINT_CLASS). Apply `color=` / `status=` (on the bar or a tinted ancestor scope) to recolour the fill and track together — reach for a per-property hook only for a single surgical change. Override these hooks at `:root` (or any ancestor scope) to retheme.
|
|
46
|
+
|
|
47
|
+
| Variable | Styles | Default |
|
|
48
|
+
|---|---|---|
|
|
49
|
+
| `--progress-height` | Bar thickness | `0.375rem` (6px) |
|
|
50
|
+
| `--progress-radius` | Corner radius | `999px` (pill) |
|
|
51
|
+
| `--progress-background` | Track fill | `var(--tint-90)` |
|
|
52
|
+
| `--progress-color` | Fill colour | `var(--tint-50)` |
|
|
53
|
+
|
|
54
|
+
**Global tokens it reads** — move these to retheme broadly rather than overriding the hooks directly: the tint-ladder steps `--tint-50` (fill) and `--tint-90` (track), plus `--duration-fast` (the fill's grow transition). To recolour a bar, prefer `color=` / `status=` (or a tinted ancestor scope) over the per-component hooks.
|
|
55
|
+
|
|
56
|
+
```css
|
|
57
|
+
/* Theme: a chunkier, square-cornered bar. */
|
|
58
|
+
:root {
|
|
59
|
+
--progress-height: 0.75rem;
|
|
60
|
+
--progress-radius: var(--radius-small);
|
|
61
|
+
}
|
|
62
|
+
```
|
|
@@ -4,35 +4,32 @@
|
|
|
4
4
|
@import "../style/Tint.module.css";
|
|
5
5
|
|
|
6
6
|
@layer components {
|
|
7
|
-
/* Continuous progress bar — a single track that fills horizontally. */
|
|
8
|
-
.
|
|
9
|
-
|
|
7
|
+
/* Continuous progress bar — a native `<progress>` styled as a single track that fills horizontally. */
|
|
8
|
+
.progress {
|
|
9
|
+
/* Strip the native rendering so the track/fill can be painted from tokens. */
|
|
10
|
+
appearance: none;
|
|
11
|
+
display: block;
|
|
10
12
|
margin: 0;
|
|
13
|
+
border: none;
|
|
11
14
|
inline-size: 100%;
|
|
12
|
-
|
|
15
|
+
block-size: var(--progress-height, 0.375rem);
|
|
13
16
|
border-radius: var(--progress-radius, 999px);
|
|
14
|
-
background: var(--progress-background, var(--tint-90));
|
|
15
17
|
overflow: hidden;
|
|
18
|
+
/* Track colour — Firefox uses the element itself as the track; WebKit/Blink repaints it on the pseudo-element below. */
|
|
19
|
+
background: var(--progress-background, var(--tint-90));
|
|
16
20
|
}
|
|
17
21
|
|
|
18
|
-
.
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
/* Width is set inline; clamp overspill to the track here. */
|
|
22
|
-
min-width: 0px;
|
|
23
|
-
max-width: 100%;
|
|
24
|
-
background: var(--progress-color, var(--color-blue));
|
|
25
|
-
transition: all var(--duration-fast);
|
|
22
|
+
/* Track (WebKit/Blink) — Firefox has no track pseudo-element. */
|
|
23
|
+
.progress::-webkit-progress-bar {
|
|
24
|
+
background: var(--progress-background, var(--tint-90));
|
|
26
25
|
}
|
|
27
26
|
|
|
28
|
-
/*
|
|
29
|
-
.
|
|
30
|
-
background: var(--progress-
|
|
31
|
-
|
|
32
|
-
.warning .fill {
|
|
33
|
-
background: var(--progress-warning, var(--color-orange));
|
|
27
|
+
/* Fill — WebKit/Blink and Firefox expose the fill under different pseudo-elements, so both repeat the same paint. Painted from the tint ladder, so `color=` / `status=` recolour it. */
|
|
28
|
+
.progress::-webkit-progress-value {
|
|
29
|
+
background: var(--progress-color, var(--tint-50));
|
|
30
|
+
transition: inline-size var(--duration-fast);
|
|
34
31
|
}
|
|
35
|
-
.
|
|
36
|
-
background: var(--progress-
|
|
32
|
+
.progress::-moz-progress-bar {
|
|
33
|
+
background: var(--progress-color, var(--tint-50));
|
|
37
34
|
}
|
|
38
35
|
}
|
package/ui/form/Progress.tsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ReactElement } from "react";
|
|
2
2
|
import { formatPercent } from "../../util/format.js";
|
|
3
|
-
import {
|
|
3
|
+
import { type ColorVariants, getColorClass } from "../style/Color.js";
|
|
4
|
+
import { getStatusClass, type StatusVariants } from "../style/Status.js";
|
|
4
5
|
import { getClass, getModuleClass } from "../util/css.js";
|
|
5
6
|
import styles from "./Progress.module.css";
|
|
6
7
|
|
|
@@ -9,43 +10,39 @@ import styles from "./Progress.module.css";
|
|
|
9
10
|
*
|
|
10
11
|
* @see https://shelving.cc/ui/ProgressProps
|
|
11
12
|
*/
|
|
12
|
-
export interface ProgressProps {
|
|
13
|
+
export interface ProgressProps extends ColorVariants, StatusVariants {
|
|
13
14
|
value: number;
|
|
14
15
|
min?: number;
|
|
15
16
|
max?: number;
|
|
16
|
-
success?: boolean;
|
|
17
|
-
warning?: boolean;
|
|
18
|
-
danger?: boolean;
|
|
19
17
|
}
|
|
20
18
|
|
|
21
19
|
/**
|
|
22
20
|
* Show progress as a single continuous horizontal bar, filled to `value` within the `min`–`max` range (matches `getPercent()` and `formatPercent()`).
|
|
23
|
-
* -
|
|
21
|
+
* - Renders a native `<progress>` element, so `role="progressbar"` and the value/max semantics come from the browser.
|
|
22
|
+
* - `<progress>` has no `min` attribute (its implicit minimum is `0`), so the range is normalised to `value - min` / `max - min` before it's handed to the element.
|
|
23
|
+
* - The browser clamps `value` to the `0`–`max` range, so an out-of-range `value` shows an empty or full bar rather than overspilling.
|
|
24
|
+
* - Paints from the tint ladder, so `color=` / `status=` recolour the fill (and track) by moving the tint anchor.
|
|
24
25
|
*
|
|
25
26
|
* @returns A progress bar element.
|
|
26
27
|
* @kind component
|
|
27
28
|
* @example <Progress value={3} max={4} />
|
|
29
|
+
* @example <Progress value={90} status="success" />
|
|
28
30
|
* @see https://shelving.cc/ui/Progress
|
|
29
31
|
*/
|
|
30
|
-
export function Progress({ value, min = 0, max = 100,
|
|
31
|
-
|
|
32
|
-
const
|
|
32
|
+
export function Progress({ value, min = 0, max = 100, ...props }: ProgressProps): ReactElement {
|
|
33
|
+
// `<progress>` has no `min`, so shift the range to a `0`-based one. A non-positive span would make `<progress>` indeterminate, so fall back to an empty determinate bar.
|
|
34
|
+
const span = max - min;
|
|
33
35
|
|
|
34
36
|
return (
|
|
35
|
-
<
|
|
37
|
+
<progress
|
|
36
38
|
className={getClass(
|
|
37
|
-
getModuleClass(styles, "
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
danger && getModuleClass(styles, "danger"),
|
|
39
|
+
getModuleClass(styles, "progress"), //
|
|
40
|
+
getColorClass(props),
|
|
41
|
+
getStatusClass(props),
|
|
41
42
|
)}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
aria-valuemax={max}
|
|
45
|
-
aria-valuenow={value}
|
|
43
|
+
value={span > 0 ? value - min : 0}
|
|
44
|
+
max={span > 0 ? span : 1}
|
|
46
45
|
aria-valuetext={formatPercent(value - min, max - min)}
|
|
47
|
-
|
|
48
|
-
<span className={getModuleClass(styles, "fill")} style={fillStyle} />
|
|
49
|
-
</figure>
|
|
46
|
+
/>
|
|
50
47
|
);
|
|
51
48
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import type { ReactElement } from "react";
|
|
2
|
-
import type
|
|
3
|
-
import type
|
|
4
|
-
import type
|
|
2
|
+
import { type IndentVariants } from "../style/Indent.js";
|
|
3
|
+
import { type PaddingVariants } from "../style/Padding.js";
|
|
4
|
+
import { type WidthVariants } from "../style/Width.js";
|
|
5
5
|
import type { OptionalChildProps } from "../util/props.js";
|
|
6
6
|
/**
|
|
7
|
-
* Props for `<CenteredLayout>` — optional `children`
|
|
7
|
+
* Props for `<CenteredLayout>` — optional `children` plus `width` (of the centred column) and `padding` / `indent`
|
|
8
|
+
* (block / inline padding of the scroll area) variants.
|
|
8
9
|
*
|
|
9
10
|
* @see https://shelving.cc/ui/CenteredLayoutProps
|
|
10
11
|
*/
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { RouteCache } from "../router/RouteCache.js";
|
|
3
|
-
import {
|
|
3
|
+
import { getIndentClass } from "../style/Indent.js";
|
|
4
|
+
import { getPaddingClass } from "../style/Padding.js";
|
|
5
|
+
import { getWidthClass } from "../style/Width.js";
|
|
4
6
|
import { getClass, getModuleClass } from "../util/css.js";
|
|
5
7
|
import LAYOUT_CSS from "./CenteredLayout.module.css";
|
|
6
8
|
const LAYOUT_MAIN_CLASS = getModuleClass(LAYOUT_CSS, "main");
|
|
@@ -15,6 +17,12 @@ const LAYOUT_INNER_CLASS = getModuleClass(LAYOUT_CSS, "inner");
|
|
|
15
17
|
export function CenteredLayout({ children, ...props }) {
|
|
16
18
|
// Wrap the scrolling `<main>` in `<RouteCache>` so recently-visited pages stay mounted but hidden,
|
|
17
19
|
// keeping their scroll position and state intact across back/forward navigation.
|
|
18
|
-
|
|
19
|
-
|
|
20
|
+
//
|
|
21
|
+
// The `padding` / `indent` variants set the scroll area's block / inline padding on `<main>`, while the `width`
|
|
22
|
+
// variant sizes the centred `.inner` column. The column stays free of variant/block classes so its `margin: auto`
|
|
23
|
+
// (which does the vertical + horizontal centring) is never zeroed by the `:first-child` / `:last-child` margin
|
|
24
|
+
// collapses that `.block` carries in `@layer overrides`.
|
|
25
|
+
return (_jsx(RouteCache, { children: _jsx("main", { className: getClass(LAYOUT_MAIN_CLASS, //
|
|
26
|
+
getPaddingClass(props), getIndentClass(props)), children: _jsx("div", { className: getClass(LAYOUT_INNER_CLASS, //
|
|
27
|
+
getWidthClass(props)), children: children }) }) }));
|
|
20
28
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
# CenteredLayout
|
|
2
2
|
|
|
3
|
-
A full-viewport layout that centres its content horizontally inside a narrow max-width column. Good for login, registration, error, and other focused single-purpose pages where the content is the only thing on screen.
|
|
3
|
+
A full-viewport layout that centres its content — vertically and horizontally — inside a narrow max-width column. Good for login, registration, error, and other focused single-purpose pages where the content is the only thing on screen. When the content is taller than the viewport it scrolls vertically, pinned to the top so nothing is clipped.
|
|
4
4
|
|
|
5
5
|
**Things to know:**
|
|
6
6
|
|
|
7
|
-
- Pass
|
|
7
|
+
- Pass the `width` variant to resize the centred column (e.g. `width="normal"`, `width="wide"`, or `width="full"` to fill the available width).
|
|
8
|
+
- Pass the `padding` (block / top + bottom) and `indent` (inline / left + right) variants to change the space around the content.
|
|
8
9
|
- Like the other full-viewport layouts it owns scroll, padding, and safe-area insets so individual pages don't have to.
|
|
9
10
|
|
|
10
11
|
## Usage
|
|
@@ -14,8 +15,8 @@ import { CenteredLayout, Section } from "shelving/ui";
|
|
|
14
15
|
|
|
15
16
|
function LoginPage() {
|
|
16
17
|
return (
|
|
17
|
-
<CenteredLayout>
|
|
18
|
-
<Section
|
|
18
|
+
<CenteredLayout width="narrow">
|
|
19
|
+
<Section>
|
|
19
20
|
<LoginForm/>
|
|
20
21
|
</Section>
|
|
21
22
|
</CenteredLayout>
|
|
@@ -29,11 +30,12 @@ Layouts compose naturally as `<Router>` route values — wrap a group of routes
|
|
|
29
30
|
|
|
30
31
|
| Variable | Styles | Default |
|
|
31
32
|
|---|---|---|
|
|
32
|
-
| `--centered-layout-width` |
|
|
33
|
-
| `--centered-layout-
|
|
34
|
-
| `--centered-layout-
|
|
35
|
-
| `--centered-layout-background` | Page background while the layout is mounted |
|
|
33
|
+
| `--centered-layout-width` | Width of the centred column (capped at 100%) | `var(--width-narrow)` |
|
|
34
|
+
| `--centered-layout-padding` | Block (top/bottom) padding of the scroll area | `var(--space-normal)` |
|
|
35
|
+
| `--centered-layout-indent` | Inline (left/right) padding of the scroll area | `var(--space-normal)` |
|
|
36
|
+
| `--centered-layout-background` | Page background while the layout is mounted | `var(--tint-100)` (white) |
|
|
37
|
+
| `--centered-layout-color` | Text colour for the layout | `var(--tint-00)` (black) |
|
|
36
38
|
|
|
37
|
-
The
|
|
39
|
+
The column width and the scroll-area padding can also be set per-instance via the `width`, `padding`, and `indent` variant props. The outer element owns its scroll, padding, and safe-area behaviour directly — safe-area insets are applied as transparent borders so they stack with (rather than replace) the padding.
|
|
38
40
|
|
|
39
|
-
**Global tokens it reads** — `--width-narrow
|
|
41
|
+
**Global tokens it reads** — `--width-narrow`, `--space-normal`, `--tint-100`, and `--tint-00`.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
@import "../style/layers.css";
|
|
2
2
|
@import "../style/Space.module.css";
|
|
3
|
+
@import "../style/Tint.module.css";
|
|
3
4
|
@import "../style/Width.module.css";
|
|
4
5
|
|
|
5
6
|
@layer components {
|
|
@@ -34,8 +35,9 @@
|
|
|
34
35
|
position: fixed;
|
|
35
36
|
inset: 0;
|
|
36
37
|
|
|
37
|
-
/*
|
|
38
|
-
background: var(--centered-layout-background);
|
|
38
|
+
/* Page background + text colour; fall back to the themed page defaults (--tint-100 / --tint-00) when the hooks are unset. */
|
|
39
|
+
background: var(--centered-layout-background, var(--tint-100));
|
|
40
|
+
color: var(--centered-layout-color, var(--tint-00));
|
|
39
41
|
}
|
|
40
42
|
|
|
41
43
|
.main {
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import type { ReactElement } from "react";
|
|
2
2
|
import { RouteCache } from "../router/RouteCache.js";
|
|
3
|
-
import {
|
|
4
|
-
import type
|
|
5
|
-
import type
|
|
6
|
-
import type { WidthVariants } from "../style/Width.js";
|
|
3
|
+
import { getIndentClass, type IndentVariants } from "../style/Indent.js";
|
|
4
|
+
import { getPaddingClass, type PaddingVariants } from "../style/Padding.js";
|
|
5
|
+
import { getWidthClass, type WidthVariants } from "../style/Width.js";
|
|
7
6
|
import { getClass, getModuleClass } from "../util/css.js";
|
|
8
7
|
import type { OptionalChildProps } from "../util/props.js";
|
|
9
8
|
import LAYOUT_CSS from "./CenteredLayout.module.css";
|
|
@@ -12,7 +11,8 @@ const LAYOUT_MAIN_CLASS = getModuleClass(LAYOUT_CSS, "main");
|
|
|
12
11
|
const LAYOUT_INNER_CLASS = getModuleClass(LAYOUT_CSS, "inner");
|
|
13
12
|
|
|
14
13
|
/**
|
|
15
|
-
* Props for `<CenteredLayout>` — optional `children`
|
|
14
|
+
* Props for `<CenteredLayout>` — optional `children` plus `width` (of the centred column) and `padding` / `indent`
|
|
15
|
+
* (block / inline padding of the scroll area) variants.
|
|
16
16
|
*
|
|
17
17
|
* @see https://shelving.cc/ui/CenteredLayoutProps
|
|
18
18
|
*/
|
|
@@ -28,13 +28,24 @@ export interface CenteredLayoutProps extends WidthVariants, PaddingVariants, Ind
|
|
|
28
28
|
export function CenteredLayout({ children, ...props }: CenteredLayoutProps): ReactElement {
|
|
29
29
|
// Wrap the scrolling `<main>` in `<RouteCache>` so recently-visited pages stay mounted but hidden,
|
|
30
30
|
// keeping their scroll position and state intact across back/forward navigation.
|
|
31
|
+
//
|
|
32
|
+
// The `padding` / `indent` variants set the scroll area's block / inline padding on `<main>`, while the `width`
|
|
33
|
+
// variant sizes the centred `.inner` column. The column stays free of variant/block classes so its `margin: auto`
|
|
34
|
+
// (which does the vertical + horizontal centring) is never zeroed by the `:first-child` / `:last-child` margin
|
|
35
|
+
// collapses that `.block` carries in `@layer overrides`.
|
|
31
36
|
return (
|
|
32
37
|
<RouteCache>
|
|
33
|
-
<main
|
|
38
|
+
<main
|
|
39
|
+
className={getClass(
|
|
40
|
+
LAYOUT_MAIN_CLASS, //
|
|
41
|
+
getPaddingClass(props),
|
|
42
|
+
getIndentClass(props),
|
|
43
|
+
)}
|
|
44
|
+
>
|
|
34
45
|
<div
|
|
35
46
|
className={getClass(
|
|
36
47
|
LAYOUT_INNER_CLASS, //
|
|
37
|
-
|
|
48
|
+
getWidthClass(props),
|
|
38
49
|
)}
|
|
39
50
|
>
|
|
40
51
|
{children}
|
|
@@ -48,11 +48,12 @@ useEffect(useSafeKeyboardArea, []);
|
|
|
48
48
|
| Variable | Styles | Default |
|
|
49
49
|
|---|---|---|
|
|
50
50
|
| `--sidebar-layout-width` | Width of the side column (and drawer) | `17.5rem` |
|
|
51
|
-
| `--sidebar-layout-background` | Page background while the layout is mounted
|
|
52
|
-
| `--sidebar-layout-
|
|
53
|
-
| `--sidebar-layout-
|
|
51
|
+
| `--sidebar-layout-background` | Page background while the layout is mounted (also fills the content column) | `var(--tint-100)` (white) |
|
|
52
|
+
| `--sidebar-layout-color` | Text colour for the layout (set on `body`, inherited by the content column) | `var(--tint-00)` (black) |
|
|
53
|
+
| `--sidebar-layout-sidebar-background` | Sidebar column fill | `var(--tint-90)` (one shade darker than the page) |
|
|
54
|
+
| `--sidebar-layout-sidebar-color` | Sidebar column text colour | `var(--tint-00)` (black) |
|
|
54
55
|
| `--sidebar-layout-border` | Divider between sidebar and content | `var(--stroke-normal) solid var(--tint-80)` |
|
|
55
56
|
|
|
56
57
|
The sidebar and content columns own their own scroll behaviour directly (this layout no longer composes a shared `.layout` class). `useSafeKeyboardArea()` still writes `--layout-inset-bottom` for layouts that pad to the safe area.
|
|
57
58
|
|
|
58
|
-
**Global tokens it reads** — `--tint-80`, plus `--space-normal`, `--stroke-normal`, `--duration-normal`, and `--color-shadow`.
|
|
59
|
+
**Global tokens it reads** — `--tint-00` / `--tint-80` / `--tint-90` / `--tint-100`, plus `--space-normal`, `--stroke-normal`, `--duration-normal`, and `--color-shadow`.
|
|
@@ -50,8 +50,10 @@
|
|
|
50
50
|
inset: 0;
|
|
51
51
|
|
|
52
52
|
/* Style */
|
|
53
|
-
/*
|
|
54
|
-
|
|
53
|
+
/* Page background + text colour; fall back to the themed page defaults (--tint-100 / --tint-00) when the hooks are unset.
|
|
54
|
+
* `color` inherits, so this one rule sets the text colour for the whole layout (including `.content`). */
|
|
55
|
+
background: var(--sidebar-layout-background, var(--tint-100));
|
|
56
|
+
color: var(--sidebar-layout-color, var(--tint-00));
|
|
55
57
|
}
|
|
56
58
|
|
|
57
59
|
.main {
|
|
@@ -75,8 +77,10 @@
|
|
|
75
77
|
padding: var(--space-normal);
|
|
76
78
|
|
|
77
79
|
/* Style */
|
|
78
|
-
/*
|
|
79
|
-
|
|
80
|
+
/* Sidebar fill + text colour; fall back to the themed defaults (--tint-90 / --tint-00) when the hooks are unset.
|
|
81
|
+
* The sidebar defaults one shade darker than the page (--tint-90 vs --tint-100) so it reads as a distinct column. */
|
|
82
|
+
background: var(--sidebar-layout-sidebar-background, var(--tint-90));
|
|
83
|
+
color: var(--sidebar-layout-sidebar-color, var(--tint-00));
|
|
80
84
|
}
|
|
81
85
|
|
|
82
86
|
.content {
|
|
@@ -93,8 +97,8 @@
|
|
|
93
97
|
scroll-behavior: smooth;
|
|
94
98
|
|
|
95
99
|
/* Style */
|
|
96
|
-
/*
|
|
97
|
-
background: var(--sidebar-layout-
|
|
100
|
+
/* Shares the page background hook; text colour is inherited from the `body` rule above (no `color` here on purpose). */
|
|
101
|
+
background: var(--sidebar-layout-background, var(--tint-100));
|
|
98
102
|
}
|
|
99
103
|
|
|
100
104
|
/* Wrapper for the menu toggle button — hidden on wide viewports, shown on narrow ones (see media query). */
|