pixelize-design-library 2.4.2-beta.30 → 2.4.2-beta.32

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.
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Button = void 0;
4
4
  const accentText_1 = require("../../Theme/tokens/builders/accentText");
5
- const color_1 = require("../../Theme/tokens/builders/color");
5
+ const solidFillRung_1 = require("../../Theme/tokens/builders/solidFillRung");
6
6
  const gradientInk_1 = require("../../Theme/tokens/builders/gradientInk");
7
7
  const focusRing_styles_1 = require("../../Theme/chakra/focusRing.styles");
8
8
  /**
@@ -22,44 +22,11 @@ const onCanvasHoverBg = (theme, colorScheme, step) => {
22
22
  const scale = theme.colors[colorScheme];
23
23
  return "opacity" in scale ? scale.opacity[step] : scale[50];
24
24
  };
25
- /**
26
- * The rung one/two steps DARKER than the accent — what a filled button's hover/active needs so its
27
- * white label stays legible.
28
- *
29
- * Every dark scale runs dark -> light across 50 -> 900 (brand scales are rebuilt that way, utility
30
- * hues are reversed), the mirror of light mode. So "darker than [500]" is [600]/[700] in light but
31
- * [400]/[300] in dark. Hardcoding [600] would hand the label a LIGHTER fill in dark and drop it to
32
- * ~3.2 contrast.
33
- */
34
- const FILL_STEP_RUNGS = { light: [600, 700], dark: [400, 300] };
35
- const RUNGS = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900];
36
- const AA_NORMAL_TEXT = 4.5;
37
- // Semantic scales (red/green) carry no opacity ramp; brand scales do and keep their own [500].
38
- const solidRung = (theme, colorScheme) => {
39
- const scale = theme.colors[colorScheme];
40
- const white = theme.colors.white;
41
- if ("opacity" in scale)
42
- return 500;
43
- if ((0, color_1.contrastRatio)(scale[500], white) >= AA_NORMAL_TEXT)
44
- return 500;
45
- const passing = RUNGS.filter((r) => scale[r] && (0, color_1.contrastRatio)(scale[r], white) >= AA_NORMAL_TEXT);
46
- if (!passing.length)
47
- return 500;
48
- return passing.reduce((a, b) => (0, color_1.contrastRatio)(scale[a], white) <= (0, color_1.contrastRatio)(scale[b], white) ? a : b);
49
- };
50
- const steppedRung = (theme, base, step) => {
51
- const dir = (0, accentText_1.isDarkCanvas)(theme.colors.backgroundColor.main) ? -100 : 100;
52
- return Math.min(900, Math.max(50, base + dir * step));
53
- };
25
+ const buttonSolidRung = (theme, colorScheme) => (0, solidFillRung_1.solidRung)(theme.colors[colorScheme], theme.colors.white);
54
26
  const darkerFill = (theme, colorScheme, step) => {
55
- var _a;
56
27
  const scale = theme.colors[colorScheme];
57
- const base = solidRung(theme, colorScheme);
58
- if (base === 500) {
59
- const mode = (0, accentText_1.isDarkCanvas)(theme.colors.backgroundColor.main) ? "dark" : "light";
60
- return scale[FILL_STEP_RUNGS[mode][step]];
61
- }
62
- return (_a = scale[steppedRung(theme, base, step === 0 ? 1 : 2)]) !== null && _a !== void 0 ? _a : scale[base];
28
+ const isDark = (0, accentText_1.isDarkCanvas)(theme.colors.backgroundColor.main);
29
+ return scale[(0, solidFillRung_1.darkerFillRung)(scale, theme.colors.white, isDark, step)];
63
30
  };
64
31
  /** Label for the solid variant's filled bg — derived from THAT fill, since hover/active swap it. */
65
32
  const labelFor = (theme, fill) => (0, accentText_1.onFilled)(fill, theme.colors.white, theme.colors.black);
@@ -88,7 +55,7 @@ exports.Button = {
88
55
  }),
89
56
  variants: {
90
57
  solid: ({ theme, colorScheme = "primary" }) => {
91
- const fill = theme.colors[colorScheme][solidRung(theme, colorScheme)];
58
+ const fill = theme.colors[colorScheme][buttonSolidRung(theme, colorScheme)];
92
59
  const hoverFill = darkerFill(theme, colorScheme, 0);
93
60
  const activeFill = darkerFill(theme, colorScheme, 1);
94
61
  const hoverLabel = labelFor(theme, hoverFill);
@@ -122,7 +89,7 @@ exports.Button = {
122
89
  },
123
90
  outline: ({ theme, colorScheme = "primary" }) => {
124
91
  // Same rung as `solid`'s fill, not raw [500] — keeps hover bg AA-safe for white text.
125
- const hoverFill = theme.colors[colorScheme][solidRung(theme, colorScheme)];
92
+ const hoverFill = theme.colors[colorScheme][buttonSolidRung(theme, colorScheme)];
126
93
  const activeFill = darkerFill(theme, colorScheme, 0);
127
94
  const ink = onCanvasText(theme, colorScheme);
128
95
  return {
@@ -18,6 +18,7 @@ const TableHeader_1 = __importDefault(require("./components/TableHeader"));
18
18
  const TableBody_1 = __importDefault(require("./components/TableBody"));
19
19
  const useCustomTheme_1 = require("../../Theme/useCustomTheme");
20
20
  const scrollbar_1 = require("../../Theme/tokens/builders/scrollbar");
21
+ const scrollShadow_1 = require("../../Theme/tokens/builders/scrollShadow");
21
22
  const useTable_1 = __importDefault(require("./hooks/useTable"));
22
23
  const table_1 = require("../../Utils/table");
23
24
  const usePreferences_1 = require("../../Hooks/usePreferences");
@@ -399,6 +400,7 @@ function Table({ data, columns, onSelection, isLoading, isCheckbox = false, head
399
400
  }, children: (0, jsx_runtime_1.jsx)(lucide_react_1.RefreshCw, { size: 16 }) }) })] })), headerActions && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(Divider_1.default, {}), (0, jsx_runtime_1.jsx)(HeaderActions_1.default, { actions: headerActions, selections: selection })] })), (0, jsx_runtime_1.jsx)(ActiveFilters_1.default, { columns: columnsList, columnsSearch: columnsSearch, setColumnsSearch: setColumnsSearch }), (0, jsx_runtime_1.jsxs)(react_2.Box, { ml: "auto", display: "flex", alignItems: "center", gap: 2, children: [groupLoadMoreActive && loadMorePosition === "top" && (groupLoadMoreCaption || canGroupLoadMore) && ((0, jsx_runtime_1.jsxs)(react_2.Box, { display: "flex", alignItems: "center", gap: 2, flex: "0 0 auto", children: [groupLoadMoreCaption && ((0, jsx_runtime_1.jsx)(react_2.Box, { fontSize: "0.75rem", color: theme.colors.text[500], whiteSpace: "nowrap", children: groupLoadMoreCaption })), canGroupLoadMore && ((0, jsx_runtime_1.jsx)(Button_1.default, { size: "xs", variant: "outline", colorScheme: "gray", isLoading: isLoadingMore, loadingText: loadMoreText, onClick: handleGroupLoadMore, label: loadMoreText }))] })), (isPagination || isServerPagination) && !isGrouped && !canInfinite && !isCompactHeader && ((0, jsx_runtime_1.jsx)("div", { children: (0, jsx_runtime_1.jsx)(Pagination_1.default, { columns: columns, currentPage: currentPage, setCurrentPage: setCurrentPage, rowsPerPage: rowsPerPage, pages: pages, paginationText: tablePaginationText, handlePageSizeChange: handlePageSizeChange, dataLength: tableData.length, isServerPagination: isServerPagination, paginationSelectOptions: paginationSelectOptions, isVisiblity: true }) })), (isPagination || isServerPagination) && !isGrouped && !canInfinite && isCompactHeader && tableData.length > 0 && ((0, jsx_runtime_1.jsxs)(react_2.Popover, { isOpen: isPaginationPopoverOpen, onClose: () => setIsPaginationPopoverOpen(false), placement: "bottom-end", children: [(0, jsx_runtime_1.jsx)(react_2.PopoverTrigger, { children: (0, jsx_runtime_1.jsx)(react_2.IconButton, { "aria-label": "Pagination", size: "sm", variant: "ghost", icon: (0, jsx_runtime_1.jsx)(lucide_react_1.EllipsisVertical, { size: 18 }), color: theme.colors.text[500], _hover: {
400
401
  color: theme.colors.accentText,
401
402
  }, onClick: () => setIsPaginationPopoverOpen((open) => !open) }) }), (0, jsx_runtime_1.jsx)(react_2.Portal, { children: (0, jsx_runtime_1.jsx)(react_2.PopoverContent, { maxW: "22rem", p: 2, zIndex: 1600, bg: theme.colors.background[50], boxShadow: "lg", children: (0, jsx_runtime_1.jsx)(react_2.PopoverBody, { p: 0, children: (0, jsx_runtime_1.jsx)(Pagination_1.default, { columns: columns, currentPage: currentPage, setCurrentPage: setCurrentPage, rowsPerPage: rowsPerPage, pages: pages, paginationText: tablePaginationText, handlePageSizeChange: handlePageSizeChange, dataLength: tableData.length, isServerPagination: isServerPagination, paginationSelectOptions: paginationSelectOptions, isVisiblity: true }) }) }) })] }))] })] }), (0, jsx_runtime_1.jsxs)(react_2.TableContainer, { ref: tableContainerRef, position: "relative", h: isTableLoading ? tableMaxH : undefined, maxH: !isTableLoading ? tableMaxH : undefined, pb: isTableLoading ? 0 : compactScrollbarPadding, overflowY: isCompactRows ? "hidden" : "auto", overflowX: "auto", sx: {
403
+ ...(0, scrollShadow_1.scrollShadowStyles)(theme.colors.background[50]),
402
404
  ...(0, scrollbar_1.scrollbarStyles)(theme.colors),
403
405
  // Compact rows hide the horizontal bar until hover to keep the row height.
404
406
  '&::-webkit-scrollbar': {
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Tag = void 0;
4
4
  const accentText_1 = require("../../Theme/tokens/builders/accentText");
5
+ const solidFillRung_1 = require("../../Theme/tokens/builders/solidFillRung");
5
6
  // Resolve a colorScheme key to a numeric swatch from the theme. All supported
6
7
  // colorScheme values (brand + global scales) expose a 50-900 scale; fall back to
7
8
  // gray so an unknown/missing key can never throw at render time.
@@ -42,10 +43,11 @@ exports.Tag = {
42
43
  variants: {
43
44
  solid: ({ theme, colorScheme = "primary" }) => {
44
45
  const c = swatch(theme, colorScheme);
45
- const { white, black } = theme.colors;
46
- const fill = c[500];
47
- const hoverFill = c[600];
48
- const activeFill = c[700];
46
+ const { white, black, backgroundColor } = theme.colors;
47
+ const isDark = (0, accentText_1.isDarkCanvas)(backgroundColor.main);
48
+ const fill = c[(0, solidFillRung_1.solidRung)(c, white)];
49
+ const hoverFill = c[(0, solidFillRung_1.darkerFillRung)(c, white, isDark, 0)];
50
+ const activeFill = c[(0, solidFillRung_1.darkerFillRung)(c, white, isDark, 1)];
49
51
  return {
50
52
  container: {
51
53
  bg: fill,
@@ -14,6 +14,7 @@ const NumberInput_styles_1 = require("../../Components/NumberInput/NumberInput.s
14
14
  const accentText_1 = require("../tokens/builders/accentText");
15
15
  const color_1 = require("../tokens/builders/color");
16
16
  const outlineRung_1 = require("../tokens/builders/outlineRung");
17
+ const solidFillRung_1 = require("../tokens/builders/solidFillRung");
17
18
  const Drawer_styles_1 = require("./Drawer.styles");
18
19
  const Menu_styles_1 = require("./Menu.styles");
19
20
  const Modal_styles_1 = require("./Modal.styles");
@@ -34,11 +35,10 @@ const Badge = {
34
35
  solid: ({ theme, colorScheme = "gray" }) => {
35
36
  const palette = (0, paletteFromTheme_1.paletteOf)(theme);
36
37
  const swatch = (0, paletteFromTheme_1.scaleFor)(palette, colorScheme);
37
- const bg = swatch[500];
38
- return {
39
- bg,
40
- color: isHexColor(bg) ? (0, accentText_1.onFilled)(bg, palette.white, palette.black) : swatch[800],
41
- };
38
+ if (!isHexColor(swatch[500]))
39
+ return { bg: swatch[500], color: swatch[800] };
40
+ const bg = swatch[(0, solidFillRung_1.solidRung)(swatch, palette.white)];
41
+ return { bg, color: (0, accentText_1.onFilled)(bg, palette.white, palette.black) };
42
42
  },
43
43
  // Chakra's stock `subtle` picks its bg/color pair via `mode()`, keyed to Chakra's own
44
44
  // colorMode — which this app never toggles (dark mode swaps the palette object instead), so
@@ -0,0 +1,2 @@
1
+ export type ScrollShadowStyle = Record<string, unknown>;
2
+ export declare function scrollShadowStyles(surface: string): ScrollShadowStyle;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.scrollShadowStyles = scrollShadowStyles;
4
+ const EDGE = "24px";
5
+ const TINT = "rgba(0, 0, 0, 0.08)";
6
+ // CSS-only overflow hint via background-attachment local vs scroll — no listener, no extra node.
7
+ function scrollShadowStyles(surface) {
8
+ return {
9
+ backgroundColor: surface,
10
+ backgroundImage: `linear-gradient(to right, ${surface}, ${surface}), linear-gradient(to left, ${surface}, ${surface}), linear-gradient(to right, ${TINT}, transparent), linear-gradient(to left, ${TINT}, transparent)`,
11
+ backgroundRepeat: "no-repeat",
12
+ backgroundSize: `${EDGE} 100%, ${EDGE} 100%, 8px 100%, 8px 100%`,
13
+ backgroundPosition: "0 0, 100% 0, 0 0, 100% 0",
14
+ backgroundAttachment: "local, local, scroll, scroll",
15
+ };
16
+ }
@@ -0,0 +1,4 @@
1
+ import type { ColorScale, BrandScale } from "../types";
2
+ export type Rung = keyof ColorScale;
3
+ export declare const solidRung: (scale: ColorScale | BrandScale, white: string) => Rung;
4
+ export declare const darkerFillRung: (scale: ColorScale | BrandScale, white: string, isDark: boolean, step: 0 | 1) => Rung;
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.darkerFillRung = exports.solidRung = void 0;
4
+ const color_1 = require("./color");
5
+ const AA_NORMAL_TEXT = 4.5;
6
+ const RUNGS = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900];
7
+ const FILL_STEP_RUNGS = { light: [600, 700], dark: [400, 300] };
8
+ const hasOpacity = (scale) => "opacity" in scale;
9
+ // Brand scales keep [500]; utility scales (red, green, …) step to the closest passing rung.
10
+ const solidRung = (scale, white) => {
11
+ if (hasOpacity(scale))
12
+ return 500;
13
+ if ((0, color_1.contrastRatio)(scale[500], white) >= AA_NORMAL_TEXT)
14
+ return 500;
15
+ const passing = RUNGS.filter((r) => scale[r] && (0, color_1.contrastRatio)(scale[r], white) >= AA_NORMAL_TEXT);
16
+ if (!passing.length)
17
+ return 500;
18
+ return passing.reduce((a, b) => (0, color_1.contrastRatio)(scale[a], white) <= (0, color_1.contrastRatio)(scale[b], white) ? a : b);
19
+ };
20
+ exports.solidRung = solidRung;
21
+ const steppedRung = (base, step, isDark) => {
22
+ const dir = isDark ? -100 : 100;
23
+ return Math.min(900, Math.max(50, base + dir * step));
24
+ };
25
+ const darkerFillRung = (scale, white, isDark, step) => {
26
+ const base = (0, exports.solidRung)(scale, white);
27
+ if (base === 500) {
28
+ const mode = isDark ? "dark" : "light";
29
+ return FILL_STEP_RUNGS[mode][step];
30
+ }
31
+ return steppedRung(base, step === 0 ? 1 : 2, isDark);
32
+ };
33
+ exports.darkerFillRung = darkerFillRung;
@@ -1,5 +1,5 @@
1
1
  import { accentTextOnCanvas, isDarkCanvas, onFilled } from "../../Theme/tokens/builders/accentText.js";
2
- import { contrastRatio } from "../../Theme/tokens/builders/color.js";
2
+ import { solidRung, darkerFillRung } from "../../Theme/tokens/builders/solidFillRung.js";
3
3
  import { resolveGradient, gradientCss } from "../../Theme/tokens/builders/gradientInk.js";
4
4
  import { focusVisibleStyles } from "../../Theme/chakra/focusRing.styles.js";
5
5
  /**
@@ -19,44 +19,11 @@ const onCanvasHoverBg = (theme, colorScheme, step) => {
19
19
  const scale = theme.colors[colorScheme];
20
20
  return "opacity" in scale ? scale.opacity[step] : scale[50];
21
21
  };
22
- /**
23
- * The rung one/two steps DARKER than the accent — what a filled button's hover/active needs so its
24
- * white label stays legible.
25
- *
26
- * Every dark scale runs dark -> light across 50 -> 900 (brand scales are rebuilt that way, utility
27
- * hues are reversed), the mirror of light mode. So "darker than [500]" is [600]/[700] in light but
28
- * [400]/[300] in dark. Hardcoding [600] would hand the label a LIGHTER fill in dark and drop it to
29
- * ~3.2 contrast.
30
- */
31
- const FILL_STEP_RUNGS = { light: [600, 700], dark: [400, 300] };
32
- const RUNGS = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900];
33
- const AA_NORMAL_TEXT = 4.5;
34
- // Semantic scales (red/green) carry no opacity ramp; brand scales do and keep their own [500].
35
- const solidRung = (theme, colorScheme) => {
36
- const scale = theme.colors[colorScheme];
37
- const white = theme.colors.white;
38
- if ("opacity" in scale)
39
- return 500;
40
- if (contrastRatio(scale[500], white) >= AA_NORMAL_TEXT)
41
- return 500;
42
- const passing = RUNGS.filter((r) => scale[r] && contrastRatio(scale[r], white) >= AA_NORMAL_TEXT);
43
- if (!passing.length)
44
- return 500;
45
- return passing.reduce((a, b) => contrastRatio(scale[a], white) <= contrastRatio(scale[b], white) ? a : b);
46
- };
47
- const steppedRung = (theme, base, step) => {
48
- const dir = isDarkCanvas(theme.colors.backgroundColor.main) ? -100 : 100;
49
- return Math.min(900, Math.max(50, base + dir * step));
50
- };
22
+ const buttonSolidRung = (theme, colorScheme) => solidRung(theme.colors[colorScheme], theme.colors.white);
51
23
  const darkerFill = (theme, colorScheme, step) => {
52
- var _a;
53
24
  const scale = theme.colors[colorScheme];
54
- const base = solidRung(theme, colorScheme);
55
- if (base === 500) {
56
- const mode = isDarkCanvas(theme.colors.backgroundColor.main) ? "dark" : "light";
57
- return scale[FILL_STEP_RUNGS[mode][step]];
58
- }
59
- return (_a = scale[steppedRung(theme, base, step === 0 ? 1 : 2)]) !== null && _a !== void 0 ? _a : scale[base];
25
+ const isDark = isDarkCanvas(theme.colors.backgroundColor.main);
26
+ return scale[darkerFillRung(scale, theme.colors.white, isDark, step)];
60
27
  };
61
28
  /** Label for the solid variant's filled bg — derived from THAT fill, since hover/active swap it. */
62
29
  const labelFor = (theme, fill) => onFilled(fill, theme.colors.white, theme.colors.black);
@@ -85,7 +52,7 @@ export const Button = {
85
52
  }),
86
53
  variants: {
87
54
  solid: ({ theme, colorScheme = "primary" }) => {
88
- const fill = theme.colors[colorScheme][solidRung(theme, colorScheme)];
55
+ const fill = theme.colors[colorScheme][buttonSolidRung(theme, colorScheme)];
89
56
  const hoverFill = darkerFill(theme, colorScheme, 0);
90
57
  const activeFill = darkerFill(theme, colorScheme, 1);
91
58
  const hoverLabel = labelFor(theme, hoverFill);
@@ -119,7 +86,7 @@ export const Button = {
119
86
  },
120
87
  outline: ({ theme, colorScheme = "primary" }) => {
121
88
  // Same rung as `solid`'s fill, not raw [500] — keeps hover bg AA-safe for white text.
122
- const hoverFill = theme.colors[colorScheme][solidRung(theme, colorScheme)];
89
+ const hoverFill = theme.colors[colorScheme][buttonSolidRung(theme, colorScheme)];
123
90
  const activeFill = darkerFill(theme, colorScheme, 0);
124
91
  const ink = onCanvasText(theme, colorScheme);
125
92
  return {
@@ -12,6 +12,7 @@ import TableHeader from "./components/TableHeader.js";
12
12
  import TableBody from "./components/TableBody.js";
13
13
  import { useCustomTheme } from "../../Theme/useCustomTheme.js";
14
14
  import { scrollbarStyles, SCROLLBAR_WIDTH } from "../../Theme/tokens/builders/scrollbar.js";
15
+ import { scrollShadowStyles } from "../../Theme/tokens/builders/scrollShadow.js";
15
16
  import useTable from "./hooks/useTable.js";
16
17
  import { searchAndSortData, SortMultiColumnData, groupRows, buildTablePalette, pickTableColor } from "../../Utils/table.js";
17
18
  import { useGetPreferences, useSavePreferences, } from "../../Hooks/usePreferences.js";
@@ -393,6 +394,7 @@ export default function Table({ data, columns, onSelection, isLoading, isCheckbo
393
394
  }, children: _jsx(RefreshCw, { size: 16 }) }) })] })), headerActions && (_jsxs(_Fragment, { children: [_jsx(VeritcalDivider, {}), _jsx(HeaderActions, { actions: headerActions, selections: selection })] })), _jsx(ActiveFilters, { columns: columnsList, columnsSearch: columnsSearch, setColumnsSearch: setColumnsSearch }), _jsxs(Box, { ml: "auto", display: "flex", alignItems: "center", gap: 2, children: [groupLoadMoreActive && loadMorePosition === "top" && (groupLoadMoreCaption || canGroupLoadMore) && (_jsxs(Box, { display: "flex", alignItems: "center", gap: 2, flex: "0 0 auto", children: [groupLoadMoreCaption && (_jsx(Box, { fontSize: "0.75rem", color: theme.colors.text[500], whiteSpace: "nowrap", children: groupLoadMoreCaption })), canGroupLoadMore && (_jsx(Button, { size: "xs", variant: "outline", colorScheme: "gray", isLoading: isLoadingMore, loadingText: loadMoreText, onClick: handleGroupLoadMore, label: loadMoreText }))] })), (isPagination || isServerPagination) && !isGrouped && !canInfinite && !isCompactHeader && (_jsx("div", { children: _jsx(Pagination, { columns: columns, currentPage: currentPage, setCurrentPage: setCurrentPage, rowsPerPage: rowsPerPage, pages: pages, paginationText: tablePaginationText, handlePageSizeChange: handlePageSizeChange, dataLength: tableData.length, isServerPagination: isServerPagination, paginationSelectOptions: paginationSelectOptions, isVisiblity: true }) })), (isPagination || isServerPagination) && !isGrouped && !canInfinite && isCompactHeader && tableData.length > 0 && (_jsxs(Popover, { isOpen: isPaginationPopoverOpen, onClose: () => setIsPaginationPopoverOpen(false), placement: "bottom-end", children: [_jsx(PopoverTrigger, { children: _jsx(IconButton, { "aria-label": "Pagination", size: "sm", variant: "ghost", icon: _jsx(EllipsisVertical, { size: 18 }), color: theme.colors.text[500], _hover: {
394
395
  color: theme.colors.accentText,
395
396
  }, onClick: () => setIsPaginationPopoverOpen((open) => !open) }) }), _jsx(Portal, { children: _jsx(PopoverContent, { maxW: "22rem", p: 2, zIndex: 1600, bg: theme.colors.background[50], boxShadow: "lg", children: _jsx(PopoverBody, { p: 0, children: _jsx(Pagination, { columns: columns, currentPage: currentPage, setCurrentPage: setCurrentPage, rowsPerPage: rowsPerPage, pages: pages, paginationText: tablePaginationText, handlePageSizeChange: handlePageSizeChange, dataLength: tableData.length, isServerPagination: isServerPagination, paginationSelectOptions: paginationSelectOptions, isVisiblity: true }) }) }) })] }))] })] }), _jsxs(TableContainer, { ref: tableContainerRef, position: "relative", h: isTableLoading ? tableMaxH : undefined, maxH: !isTableLoading ? tableMaxH : undefined, pb: isTableLoading ? 0 : compactScrollbarPadding, overflowY: isCompactRows ? "hidden" : "auto", overflowX: "auto", sx: {
397
+ ...scrollShadowStyles(theme.colors.background[50]),
396
398
  ...scrollbarStyles(theme.colors),
397
399
  // Compact rows hide the horizontal bar until hover to keep the row height.
398
400
  '&::-webkit-scrollbar': {
@@ -1,4 +1,5 @@
1
- import { accentTextOnCanvas, onFilled } from "../../Theme/tokens/builders/accentText.js";
1
+ import { accentTextOnCanvas, isDarkCanvas, onFilled } from "../../Theme/tokens/builders/accentText.js";
2
+ import { solidRung, darkerFillRung } from "../../Theme/tokens/builders/solidFillRung.js";
2
3
  // Resolve a colorScheme key to a numeric swatch from the theme. All supported
3
4
  // colorScheme values (brand + global scales) expose a 50-900 scale; fall back to
4
5
  // gray so an unknown/missing key can never throw at render time.
@@ -39,10 +40,11 @@ export const Tag = {
39
40
  variants: {
40
41
  solid: ({ theme, colorScheme = "primary" }) => {
41
42
  const c = swatch(theme, colorScheme);
42
- const { white, black } = theme.colors;
43
- const fill = c[500];
44
- const hoverFill = c[600];
45
- const activeFill = c[700];
43
+ const { white, black, backgroundColor } = theme.colors;
44
+ const isDark = isDarkCanvas(backgroundColor.main);
45
+ const fill = c[solidRung(c, white)];
46
+ const hoverFill = c[darkerFillRung(c, white, isDark, 0)];
47
+ const activeFill = c[darkerFillRung(c, white, isDark, 1)];
46
48
  return {
47
49
  container: {
48
50
  bg: fill,
@@ -11,6 +11,7 @@ import { NumberInput } from "../../Components/NumberInput/NumberInput.styles.js"
11
11
  import { accentTextOnCanvas, isDarkCanvas, onFilled } from "../tokens/builders/accentText.js";
12
12
  import { contrastRatio } from "../tokens/builders/color.js";
13
13
  import { resolveHoverTint } from "../tokens/builders/outlineRung.js";
14
+ import { solidRung } from "../tokens/builders/solidFillRung.js";
14
15
  import { Drawer } from "./Drawer.styles.js";
15
16
  import { Menu } from "./Menu.styles.js";
16
17
  import { Modal } from "./Modal.styles.js";
@@ -31,11 +32,10 @@ const Badge = {
31
32
  solid: ({ theme, colorScheme = "gray" }) => {
32
33
  const palette = paletteOf(theme);
33
34
  const swatch = scaleFor(palette, colorScheme);
34
- const bg = swatch[500];
35
- return {
36
- bg,
37
- color: isHexColor(bg) ? onFilled(bg, palette.white, palette.black) : swatch[800],
38
- };
35
+ if (!isHexColor(swatch[500]))
36
+ return { bg: swatch[500], color: swatch[800] };
37
+ const bg = swatch[solidRung(swatch, palette.white)];
38
+ return { bg, color: onFilled(bg, palette.white, palette.black) };
39
39
  },
40
40
  // Chakra's stock `subtle` picks its bg/color pair via `mode()`, keyed to Chakra's own
41
41
  // colorMode — which this app never toggles (dark mode swaps the palette object instead), so
@@ -0,0 +1,13 @@
1
+ const EDGE = "24px";
2
+ const TINT = "rgba(0, 0, 0, 0.08)";
3
+ // CSS-only overflow hint via background-attachment local vs scroll — no listener, no extra node.
4
+ export function scrollShadowStyles(surface) {
5
+ return {
6
+ backgroundColor: surface,
7
+ backgroundImage: `linear-gradient(to right, ${surface}, ${surface}), linear-gradient(to left, ${surface}, ${surface}), linear-gradient(to right, ${TINT}, transparent), linear-gradient(to left, ${TINT}, transparent)`,
8
+ backgroundRepeat: "no-repeat",
9
+ backgroundSize: `${EDGE} 100%, ${EDGE} 100%, 8px 100%, 8px 100%`,
10
+ backgroundPosition: "0 0, 100% 0, 0 0, 100% 0",
11
+ backgroundAttachment: "local, local, scroll, scroll",
12
+ };
13
+ }
@@ -0,0 +1,28 @@
1
+ import { contrastRatio } from "./color.js";
2
+ const AA_NORMAL_TEXT = 4.5;
3
+ const RUNGS = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900];
4
+ const FILL_STEP_RUNGS = { light: [600, 700], dark: [400, 300] };
5
+ const hasOpacity = (scale) => "opacity" in scale;
6
+ // Brand scales keep [500]; utility scales (red, green, …) step to the closest passing rung.
7
+ export const solidRung = (scale, white) => {
8
+ if (hasOpacity(scale))
9
+ return 500;
10
+ if (contrastRatio(scale[500], white) >= AA_NORMAL_TEXT)
11
+ return 500;
12
+ const passing = RUNGS.filter((r) => scale[r] && contrastRatio(scale[r], white) >= AA_NORMAL_TEXT);
13
+ if (!passing.length)
14
+ return 500;
15
+ return passing.reduce((a, b) => contrastRatio(scale[a], white) <= contrastRatio(scale[b], white) ? a : b);
16
+ };
17
+ const steppedRung = (base, step, isDark) => {
18
+ const dir = isDark ? -100 : 100;
19
+ return Math.min(900, Math.max(50, base + dir * step));
20
+ };
21
+ export const darkerFillRung = (scale, white, isDark, step) => {
22
+ const base = solidRung(scale, white);
23
+ if (base === 500) {
24
+ const mode = isDark ? "dark" : "light";
25
+ return FILL_STEP_RUNGS[mode][step];
26
+ }
27
+ return steppedRung(base, step === 0 ? 1 : 2, isDark);
28
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pixelize-design-library",
3
- "version": "2.4.2-beta.30",
3
+ "version": "2.4.2-beta.32",
4
4
  "description": "React component library for Pixelize apps: themeable Chakra-based components, design tokens and light/dark brand theming.",
5
5
  "keywords": [
6
6
  "react",