@stapel/search-react 0.42.0 → 0.42.1

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.
@@ -0,0 +1,119 @@
1
+ /**
2
+ * The skin's scrollbar — one rule set, for every scroll port this pair owns.
3
+ *
4
+ * It started as the rail's own: `<SearchPage>` replaced the platform bar
5
+ * standing next to the filters with a 6px hairline in the token palette. But
6
+ * the rail is not the only box in the panel that scrolls — a dictionary facet
7
+ * (the make axis, 418 car makes) is a scroll port INSIDE the rail, and kept the
8
+ * platform's bar: on the live storefront the system thumb was painted over the
9
+ * count column, so «Chery 5» read as «Chery» with the 5 under a grey strip.
10
+ *
11
+ * A box that scrolls owes the reader two things, and both are here:
12
+ *
13
+ * - `scrollbar-gutter: stable`, so the space the bar needs is subtracted from
14
+ * the CONTENT box rather than overlaid on it, and the box's right edge does
15
+ * not move when the thumb arrives;
16
+ * - a thumb that is the skin's — a 6px track with no arrows and no track
17
+ * fill, transparent at rest, appearing on `:hover` (a pointer scrolling
18
+ * inside it) and `:focus-within` (a keyboard), and standing permanently
19
+ * under `(pointer: coarse)`, where neither fires and an invisible bar is a
20
+ * box with no sign it has a tail.
21
+ *
22
+ * Both vendor forms, because they are not alternatives: Firefox reads
23
+ * `scrollbar-width`/`scrollbar-color` and nothing else, WebKit and Chromium
24
+ * read the `::-webkit-scrollbar` pseudo-elements and (in Chromium) the
25
+ * standard properties too.
26
+ *
27
+ * The colours are `--stapel-*` custom properties, which resolve per theme at
28
+ * paint time — an inline colour or a `useToken()` value would freeze whichever
29
+ * theme was mounted first. This design system's neutral vocabulary has no
30
+ * `colorFill*` ramp of its own: `border` IS its tertiary-fill role (the
31
+ * hairline every pane is separated by) and `text-subtle` is that role one step
32
+ * stronger, which is what the thumb takes when a pointer is on the thumb
33
+ * itself.
34
+ *
35
+ * Emitted as one hoisted `<style href precedence>` (React 19 dedupes by
36
+ * `href`), because a pseudo-element is unreachable from an inline style — so
37
+ * the rail and the dictionary list mount the SAME element, once, however many
38
+ * of each are on screen.
39
+ *
40
+ * The names still say "rail" and they stay that way: they are this package's
41
+ * published surface, and a hoisted sheet is identified by its `href`. Renaming
42
+ * them would break every host that reads the class to assert which bar is on
43
+ * screen, and would buy nothing but a tidier word.
44
+ */
45
+ import type { CSSProperties } from "react";
46
+ import { cssVar, spacing } from "@stapel/tokens";
47
+
48
+ /**
49
+ * The class that carries the SKIN's scrollbar — present on the rail under
50
+ * `railScrollbar: "styled"` and absent under `"system"`, so the two arms are
51
+ * one class apart and a stand can read which one is on screen. Always present
52
+ * on a scroll port INSIDE the panel, which is not a surface a host chose.
53
+ */
54
+ export const RAIL_SCROLLBAR_CLASS = "stapel-search-rail-scrollbar";
55
+
56
+ /** The `href` the hoisted rail sheet is deduplicated by. */
57
+ export const RAIL_STYLE_HREF = "stapel-search-rail";
58
+
59
+ /**
60
+ * The scrollbar's track width, in CSS pixels.
61
+ *
62
+ * Not on the spacing scale on purpose, and not a spacing decision: this is the
63
+ * thickness of a hairline instrument, the size every platform's own overlay
64
+ * bar lands within, and the number the storefront's owner named. Six is thin
65
+ * enough to read as part of the panel and thick enough to grab.
66
+ */
67
+ export const RAIL_SCROLLBAR_WIDTH = 6;
68
+
69
+ /** See the module note. */
70
+ export function railScrollbarCss(): string {
71
+ const bar = `.${RAIL_SCROLLBAR_CLASS}`;
72
+ const size = `${String(RAIL_SCROLLBAR_WIDTH)}px`;
73
+ const thumb = cssVar("border");
74
+ const awake = `${bar}:hover,${bar}:focus-within`;
75
+ return [
76
+ // ── Firefox ────────────────────────────────────────────────────────────
77
+ `${bar}{scrollbar-width:thin;scrollbar-gutter:stable;` +
78
+ `scrollbar-color:transparent transparent}`,
79
+ `${awake}{scrollbar-color:${thumb} transparent}`,
80
+ // ── WebKit / Chromium ──────────────────────────────────────────────────
81
+ `${bar}::-webkit-scrollbar{inline-size:${size};block-size:${size}}`,
82
+ `${bar}::-webkit-scrollbar-track{background:transparent}`,
83
+ `${bar}::-webkit-scrollbar-thumb{background:transparent;` +
84
+ `border-radius:${cssVar("radius-full")}}`,
85
+ `${bar}:hover::-webkit-scrollbar-thumb,` +
86
+ `${bar}:focus-within::-webkit-scrollbar-thumb{background:${thumb}}`,
87
+ `${bar}::-webkit-scrollbar-thumb:hover{background:${cssVar("text-subtle")}}`,
88
+ // ── A surface with no hover at all ─────────────────────────────────────
89
+ `@media (pointer:coarse){${bar}{scrollbar-color:${thumb} transparent}` +
90
+ `${bar}::-webkit-scrollbar-thumb{background:${thumb}}}`,
91
+ ].join("\n");
92
+ }
93
+
94
+ /**
95
+ * What a scroll port inside the panel reserves at its trailing edge.
96
+ *
97
+ * `scrollbar-gutter: stable` is the rule and the sheet above states it; this
98
+ * is the same number as a value, for the ROW to keep its last column clear of.
99
+ * `scrollbar-gutter` subtracts the gutter from the scroll container's content
100
+ * box, which is enough on every engine that honours it — and is ignored
101
+ * outright by Safari, where an overlay thumb still lands on top of whatever
102
+ * sits at the trailing edge. A count is the one thing in a facet row that
103
+ * lives there, so it carries the gutter as padding of its own: correct twice
104
+ * over on Chromium and Firefox, and the only thing standing between the number
105
+ * and the thumb on WebKit.
106
+ */
107
+ export const SCROLL_GUTTER_INLINE_END: CSSProperties = {
108
+ paddingInlineEnd: RAIL_SCROLLBAR_WIDTH,
109
+ };
110
+
111
+ /**
112
+ * The top of a list that sits directly under a control.
113
+ *
114
+ * The dictionary list's first row was drawn flush against the box that filters
115
+ * it — at a glance the row read as clipped BY the input rather than as the
116
+ * first value under it. One step of the scale is the whole fix; it is stated
117
+ * here rather than inline so the rail's two scroll ports open the same way.
118
+ */
119
+ export const SCROLL_LIST_INSET_BLOCK_START: number = spacing[1];