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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shelving",
3
- "version": "1.264.2",
3
+ "version": "1.266.0",
4
4
  "author": "Dave Houlbrooke <dave@shax.com>",
5
5
  "repository": {
6
6
  "type": "git",
@@ -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
- * - The fill is allowed to overspill; CSS clamps it to the `0%`–`100%` range via `min-width`/`max-width`.
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, success, warning, danger }: ProgressProps): ReactElement | null;
27
+ export declare function Progress({ value, min, max, ...props }: ProgressProps): ReactElement;
@@ -1,19 +1,25 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { formatPercent } from "../../util/format.js";
3
- import { getPercent } from "../../util/number.js";
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
- * - The fill is allowed to overspill; CSS clamps it to the `0%`–`100%` range via `min-width`/`max-width`.
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, success, warning, danger }) {
16
- const percent = getPercent(value - min, max - min);
17
- const fillStyle = { width: `${Number.isFinite(percent) ? percent : 0}%` };
18
- return (_jsx("figure", { className: getClass(getModuleClass(styles, "track"), success && getModuleClass(styles, "success"), warning && getModuleClass(styles, "warning"), danger && getModuleClass(styles, "danger")), role: "progressbar", "aria-valuemin": min, "aria-valuemax": max, "aria-valuenow": value, "aria-valuetext": formatPercent(value - min, max - min), children: _jsx("span", { className: getModuleClass(styles, "fill"), style: fillStyle }) }));
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
- .track {
9
- position: relative;
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
- height: var(--progress-height, 0.375rem);
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
- .fill {
19
- display: block;
20
- block-size: 100%;
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
- /* Status variants applied at the container level. */
29
- .success .fill {
30
- background: var(--progress-success, var(--color-green));
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
- .danger .fill {
36
- background: var(--progress-danger, var(--color-red));
32
+ .progress::-moz-progress-bar {
33
+ background: var(--progress-color, var(--tint-50));
37
34
  }
38
35
  }
@@ -1,6 +1,7 @@
1
- import type { CSSProperties, ReactElement } from "react";
1
+ import type { ReactElement } from "react";
2
2
  import { formatPercent } from "../../util/format.js";
3
- import { getPercent } from "../../util/number.js";
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
- * - The fill is allowed to overspill; CSS clamps it to the `0%`–`100%` range via `min-width`/`max-width`.
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, success, warning, danger }: ProgressProps): ReactElement | null {
31
- const percent = getPercent(value - min, max - min);
32
- const fillStyle = { width: `${Number.isFinite(percent) ? percent : 0}%` } as CSSProperties;
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
- <figure
37
+ <progress
36
38
  className={getClass(
37
- getModuleClass(styles, "track"),
38
- success && getModuleClass(styles, "success"),
39
- warning && getModuleClass(styles, "warning"),
40
- danger && getModuleClass(styles, "danger"),
39
+ getModuleClass(styles, "progress"), //
40
+ getColorClass(props),
41
+ getStatusClass(props),
41
42
  )}
42
- role="progressbar"
43
- aria-valuemin={min}
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 { IndentVariants } from "../style/Indent.js";
3
- import type { PaddingVariants } from "../style/Padding.js";
4
- import type { WidthVariants } from "../style/Width.js";
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` and a `fullWidth` flag to drop the max-width.
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 { getBlockClass } from "../style/Block.js";
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
- return (_jsx(RouteCache, { children: _jsx("main", { className: LAYOUT_MAIN_CLASS, children: _jsx("div", { className: getClass(LAYOUT_INNER_CLASS, //
19
- getBlockClass(props)), children: children }) }) }));
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 `fullWidth` to drop the max-width constraint while keeping the centred positioning use it when the content itself needs to fill the width.
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 width="narrow">
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` | Max width of the centred column | `var(--width-narrow)` |
33
- | `--centered-layout-space` | Top/bottom padding of the scroll area | `var(--space-normal)` |
34
- | `--centered-layout-padding` | Left/right padding of the scroll area | `var(--space-normal)` |
35
- | `--centered-layout-background` | Page background while the layout is mounted | Unset — the `body` default from `Typography.module.css` shows |
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 max-width cap is dropped entirely when `fullWidth` is set. The outer element owns its scroll, padding, and safe-area behaviour directly — it also reads the `--layout-inset-top` / `-bottom` / `-left` / `-right` hooks owned by `Layout.ts` (`useSafeKeyboardArea()` writes `--layout-inset-bottom`).
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` and `--space-normal`.
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
- /* Deliberately no fallback: when the hook is unset this declaration fails and the body default from Typography.module.css shows. */
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 { getBlockClass } from "../style/Block.js";
4
- import type { IndentVariants } from "../style/Indent.js";
5
- import type { PaddingVariants } from "../style/Padding.js";
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` and a `fullWidth` flag to drop the max-width.
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 className={LAYOUT_MAIN_CLASS}>
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
- getBlockClass(props),
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 | Unset the `body` default from `Typography.module.css` shows |
52
- | `--sidebar-layout-sidebar-background` | Sidebar column fill | Unset the page background shows |
53
- | `--sidebar-layout-content-background` | Main content column fill | Unset the page background shows |
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
- /* Deliberately no fallback: when the hook is unset this declaration fails and the body default from Typography.module.css shows. */
54
- background: var(--sidebar-layout-background);
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
- /* Deliberately no fallback: when the hook is unset this declaration fails and the page background shows. */
79
- background: var(--sidebar-layout-sidebar-background);
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
- /* Deliberately no fallback: when the hook is unset this declaration fails and the page background shows. */
97
- background: var(--sidebar-layout-content-background);
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). */