From 5041e3914210e28ee3ca7dcef03dad723ba1e728 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 2 Jul 2026 20:40:39 +0000 Subject: [PATCH 1/6] feat(ScrollArea): add fadeMask prop for fading scrollable sides Uses CSS scroll-driven animations to display the fade mask without any JS: scroll timelines animate registered custom properties used as mask gradient stops, so the fade only appears on the sides that can be scrolled towards. The mask is applied to the scrollable element only, keeping the custom scrollbars unmasked. Browsers without support render the scroll area without the mask. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01VTo6CXkUBtLeftQaZ9RU4X --- .changeset/scrollarea-fade-mask.md | 5 + .../ScrollArea/ScrollArea.module.css | 113 ++++++++++++++++++ .../src/components/ScrollArea/ScrollArea.tsx | 4 +- .../components/ScrollArea/ScrollArea.types.ts | 2 + .../ScrollArea/tests/ScrollArea.stories.tsx | 52 ++++++++ 5 files changed, 175 insertions(+), 1 deletion(-) create mode 100644 .changeset/scrollarea-fade-mask.md diff --git a/.changeset/scrollarea-fade-mask.md b/.changeset/scrollarea-fade-mask.md new file mode 100644 index 00000000..2604bb80 --- /dev/null +++ b/.changeset/scrollarea-fade-mask.md @@ -0,0 +1,5 @@ +--- +"reshaped": minor +--- + +ScrollArea: Added fadeMask prop that displays a fade mask on the sides of the area that can be scrolled towards diff --git a/packages/reshaped/src/components/ScrollArea/ScrollArea.module.css b/packages/reshaped/src/components/ScrollArea/ScrollArea.module.css index 6e96ae0c..2ec6d9f5 100644 --- a/packages/reshaped/src/components/ScrollArea/ScrollArea.module.css +++ b/packages/reshaped/src/components/ScrollArea/ScrollArea.module.css @@ -1,6 +1,33 @@ +/* stylelint-disable plugin/browser-compat -- graceful degradation for browsers without scroll-driven animations support */ +@property --rs-scroll-area-fade-block-start { + syntax: ""; + initial-value: 0; + inherits: false; +} + +@property --rs-scroll-area-fade-block-end { + syntax: ""; + initial-value: 0; + inherits: false; +} + +@property --rs-scroll-area-fade-inline-start { + syntax: ""; + initial-value: 0; + inherits: false; +} + +@property --rs-scroll-area-fade-inline-end { + syntax: ""; + initial-value: 0; + inherits: false; +} +/* stylelint-enable plugin/browser-compat */ + .root { --rs-scroll-area-thumb-size: var(--rs-unit-x1); --rs-scroll-area-thumb-offset: var(--rs-unit-x1); + --rs-scroll-area-fade-size: var(--rs-unit-x6); overflow: hidden; height: 100%; @@ -26,6 +53,92 @@ } } +/* + * Fade sizes are animated by the scroll position of the scrollable element itself, + * so the mask only appears on the sides that can be scrolled towards. + * Scroll timelines stay inactive for the axes without overflow, keeping the fade sizes at 0. + * Browsers without scroll-driven animations support render the scroll area without the mask. + */ +/* stylelint-disable plugin/browser-compat -- graceful degradation for browsers without scroll-driven animations support */ +@keyframes fade-mask-block-start { + from { + --rs-scroll-area-fade-block-start: 0px; + } + + to { + --rs-scroll-area-fade-block-start: var(--rs-scroll-area-fade-size); + } +} + +@keyframes fade-mask-block-end { + from { + --rs-scroll-area-fade-block-end: var(--rs-scroll-area-fade-size); + } + + to { + --rs-scroll-area-fade-block-end: 0px; + } +} + +@keyframes fade-mask-inline-start { + from { + --rs-scroll-area-fade-inline-start: 0px; + } + + to { + --rs-scroll-area-fade-inline-start: var(--rs-scroll-area-fade-size); + } +} + +@keyframes fade-mask-inline-end { + from { + --rs-scroll-area-fade-inline-end: var(--rs-scroll-area-fade-size); + } + + to { + --rs-scroll-area-fade-inline-end: 0px; + } +} + +@supports (animation-timeline: scroll(self block)) { + .--fade-mask { + --rs-scroll-area-fade-inline-direction: to right; + + mask-image: + linear-gradient( + to bottom, + rgb(0 0 0 / 0%) 0, + rgb(0 0 0 / 100%) var(--rs-scroll-area-fade-block-start), + rgb(0 0 0 / 100%) calc(100% - var(--rs-scroll-area-fade-block-end)), + rgb(0 0 0 / 0%) 100% + ), + linear-gradient( + var(--rs-scroll-area-fade-inline-direction), + rgb(0 0 0 / 0%) 0, + rgb(0 0 0 / 100%) var(--rs-scroll-area-fade-inline-start), + rgb(0 0 0 / 100%) calc(100% - var(--rs-scroll-area-fade-inline-end)), + rgb(0 0 0 / 0%) 100% + ); + mask-composite: intersect; + animation-name: + fade-mask-block-start, fade-mask-block-end, fade-mask-inline-start, fade-mask-inline-end; + animation-timing-function: linear; + animation-fill-mode: both; + animation-timeline: + scroll(self block), scroll(self block), scroll(self inline), scroll(self inline); + animation-range: + 0 var(--rs-scroll-area-fade-size), + calc(100% - var(--rs-scroll-area-fade-size)) 100%, + 0 var(--rs-scroll-area-fade-size), + calc(100% - var(--rs-scroll-area-fade-size)) 100%; + } + + [dir="rtl"] .--fade-mask { + --rs-scroll-area-fade-inline-direction: to left; + } +} +/* stylelint-enable plugin/browser-compat */ + .--overscroll-auto { overscroll-behavior: auto; } diff --git a/packages/reshaped/src/components/ScrollArea/ScrollArea.tsx b/packages/reshaped/src/components/ScrollArea/ScrollArea.tsx index 5aae02cd..c90a30f2 100644 --- a/packages/reshaped/src/components/ScrollArea/ScrollArea.tsx +++ b/packages/reshaped/src/components/ScrollArea/ScrollArea.tsx @@ -104,6 +104,7 @@ const ScrollArea = forwardRef((props, ref) => { maxHeight, scrollbarDisplay = "hover", overscrollBehavior = "auto", + fadeMask, onScroll, className, attributes, @@ -124,7 +125,8 @@ const ScrollArea = forwardRef((props, ref) => { ); const scrollableClassNames = classNames( s.scrollable, - overscrollBehavior && s[`--overscroll-${overscrollBehavior}`] + overscrollBehavior && s[`--overscroll-${overscrollBehavior}`], + fadeMask && s["--fade-mask"] ); const updateScroll = React.useCallback(() => { diff --git a/packages/reshaped/src/components/ScrollArea/ScrollArea.types.ts b/packages/reshaped/src/components/ScrollArea/ScrollArea.types.ts index f3e3275d..7c1d464b 100644 --- a/packages/reshaped/src/components/ScrollArea/ScrollArea.types.ts +++ b/packages/reshaped/src/components/ScrollArea/ScrollArea.types.ts @@ -12,6 +12,8 @@ export type Props = { scrollbarDisplay?: "visible" | "hover" | "hidden"; /** Control whether scroll can chain to parent scrollable containers */ overscrollBehavior?: "auto" | "contain"; + /** Display a fade mask on the sides of the area that can be scrolled towards */ + fadeMask?: boolean; /** Callback when the scroll area is scrolled */ onScroll?: (args: Coordinates) => void; /** Height of the scroll area, literal css value or unit token multiplier */ diff --git a/packages/reshaped/src/components/ScrollArea/tests/ScrollArea.stories.tsx b/packages/reshaped/src/components/ScrollArea/tests/ScrollArea.stories.tsx index fc8e6227..c64d763f 100644 --- a/packages/reshaped/src/components/ScrollArea/tests/ScrollArea.stories.tsx +++ b/packages/reshaped/src/components/ScrollArea/tests/ScrollArea.stories.tsx @@ -154,6 +154,58 @@ export const overscrollBehavior = { ), }; +export const fadeMask = { + name: "fadeMask", + render: () => ( + + + + + Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum + has been the industry's standard dummy text ever since the 1500s, when an unknown + printer took a galley of type and scrambled it to make a type specimen book. It has + survived not only five centuries, but also the leap into electronic typesetting, + remaining essentially unchanged. It was popularised in the 1960s with the release of + Letraset sheets containing Lorem Ipsum passages, and more recently with desktop + publishing software like Aldus PageMaker including versions of Lorem Ipsum. + + + + + + + + Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum + has been the industry's standard dummy text ever since the 1500s + + + + + + + + Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum + has been the industry's standard dummy text ever since the 1500s, when an unknown + printer took a galley of type and scrambled it to make a type specimen book. It has + survived not only five centuries, but also the leap into electronic typesetting, + remaining essentially unchanged. It was popularised in the 1960s with the release of + Letraset sheets containing Lorem Ipsum passages, and more recently with desktop + publishing software like Aldus PageMaker including versions of Lorem Ipsum. + + + + + + + + Lorem Ipsum is simply dummy text of the printing and typesetting industry. + + + + + ), +}; + export const height = { name: "height, maxHeight", render: () => ( From 4bdd9d59e80d0dbd3925f878dd9724469896eac3 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 2 Jul 2026 20:42:04 +0000 Subject: [PATCH 2/6] chore: apply oxfmt formatting Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01VTo6CXkUBtLeftQaZ9RU4X --- README.md | 2 -- packages/theming/src/cli/run.ts | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index e2d2e4fd..f0bdd1e3 100644 --- a/README.md +++ b/README.md @@ -36,5 +36,3 @@ Read our [contribution guide](CONTRIBUTING.md) to learn about our principles, de ## License This project is licensed under the terms of the MIT license. - - diff --git a/packages/theming/src/cli/run.ts b/packages/theming/src/cli/run.ts index 71727646..4456075a 100644 --- a/packages/theming/src/cli/run.ts +++ b/packages/theming/src/cli/run.ts @@ -1,7 +1,7 @@ import fs from "node:fs"; +import { createRequire } from "node:module"; import path from "node:path"; import process from "node:process"; -import { createRequire } from "node:module"; import chalk from "chalk"; import { Command } from "commander"; From d79d963f76b6ef77f7785170d216b7a8310e3543 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 2 Jul 2026 20:48:06 +0000 Subject: [PATCH 3/6] refactor(ScrollArea): rename fadeMask prop to fade, remove comments Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01VTo6CXkUBtLeftQaZ9RU4X --- .changeset/scrollarea-fade-mask.md | 2 +- .../ScrollArea/ScrollArea.module.css | 25 ++++++------------- .../src/components/ScrollArea/ScrollArea.tsx | 4 +-- .../components/ScrollArea/ScrollArea.types.ts | 2 +- .../ScrollArea/tests/ScrollArea.stories.tsx | 20 +++++++-------- 5 files changed, 22 insertions(+), 31 deletions(-) diff --git a/.changeset/scrollarea-fade-mask.md b/.changeset/scrollarea-fade-mask.md index 2604bb80..d7606e03 100644 --- a/.changeset/scrollarea-fade-mask.md +++ b/.changeset/scrollarea-fade-mask.md @@ -2,4 +2,4 @@ "reshaped": minor --- -ScrollArea: Added fadeMask prop that displays a fade mask on the sides of the area that can be scrolled towards +ScrollArea: Added fade prop that displays a fade mask on the sides of the area that can be scrolled towards diff --git a/packages/reshaped/src/components/ScrollArea/ScrollArea.module.css b/packages/reshaped/src/components/ScrollArea/ScrollArea.module.css index 2ec6d9f5..6be3f3af 100644 --- a/packages/reshaped/src/components/ScrollArea/ScrollArea.module.css +++ b/packages/reshaped/src/components/ScrollArea/ScrollArea.module.css @@ -1,4 +1,4 @@ -/* stylelint-disable plugin/browser-compat -- graceful degradation for browsers without scroll-driven animations support */ +/* stylelint-disable plugin/browser-compat -- graceful degradation for Firefox */ @property --rs-scroll-area-fade-block-start { syntax: ""; initial-value: 0; @@ -53,14 +53,7 @@ } } -/* - * Fade sizes are animated by the scroll position of the scrollable element itself, - * so the mask only appears on the sides that can be scrolled towards. - * Scroll timelines stay inactive for the axes without overflow, keeping the fade sizes at 0. - * Browsers without scroll-driven animations support render the scroll area without the mask. - */ -/* stylelint-disable plugin/browser-compat -- graceful degradation for browsers without scroll-driven animations support */ -@keyframes fade-mask-block-start { +@keyframes fade-block-start { from { --rs-scroll-area-fade-block-start: 0px; } @@ -70,7 +63,7 @@ } } -@keyframes fade-mask-block-end { +@keyframes fade-block-end { from { --rs-scroll-area-fade-block-end: var(--rs-scroll-area-fade-size); } @@ -80,7 +73,7 @@ } } -@keyframes fade-mask-inline-start { +@keyframes fade-inline-start { from { --rs-scroll-area-fade-inline-start: 0px; } @@ -90,7 +83,7 @@ } } -@keyframes fade-mask-inline-end { +@keyframes fade-inline-end { from { --rs-scroll-area-fade-inline-end: var(--rs-scroll-area-fade-size); } @@ -101,7 +94,7 @@ } @supports (animation-timeline: scroll(self block)) { - .--fade-mask { + .--fade { --rs-scroll-area-fade-inline-direction: to right; mask-image: @@ -120,8 +113,7 @@ rgb(0 0 0 / 0%) 100% ); mask-composite: intersect; - animation-name: - fade-mask-block-start, fade-mask-block-end, fade-mask-inline-start, fade-mask-inline-end; + animation-name: fade-block-start, fade-block-end, fade-inline-start, fade-inline-end; animation-timing-function: linear; animation-fill-mode: both; animation-timeline: @@ -133,11 +125,10 @@ calc(100% - var(--rs-scroll-area-fade-size)) 100%; } - [dir="rtl"] .--fade-mask { + [dir="rtl"] .--fade { --rs-scroll-area-fade-inline-direction: to left; } } -/* stylelint-enable plugin/browser-compat */ .--overscroll-auto { overscroll-behavior: auto; diff --git a/packages/reshaped/src/components/ScrollArea/ScrollArea.tsx b/packages/reshaped/src/components/ScrollArea/ScrollArea.tsx index c90a30f2..18202892 100644 --- a/packages/reshaped/src/components/ScrollArea/ScrollArea.tsx +++ b/packages/reshaped/src/components/ScrollArea/ScrollArea.tsx @@ -104,7 +104,7 @@ const ScrollArea = forwardRef((props, ref) => { maxHeight, scrollbarDisplay = "hover", overscrollBehavior = "auto", - fadeMask, + fade, onScroll, className, attributes, @@ -126,7 +126,7 @@ const ScrollArea = forwardRef((props, ref) => { const scrollableClassNames = classNames( s.scrollable, overscrollBehavior && s[`--overscroll-${overscrollBehavior}`], - fadeMask && s["--fade-mask"] + fade && s["--fade"] ); const updateScroll = React.useCallback(() => { diff --git a/packages/reshaped/src/components/ScrollArea/ScrollArea.types.ts b/packages/reshaped/src/components/ScrollArea/ScrollArea.types.ts index 7c1d464b..d8b293c3 100644 --- a/packages/reshaped/src/components/ScrollArea/ScrollArea.types.ts +++ b/packages/reshaped/src/components/ScrollArea/ScrollArea.types.ts @@ -13,7 +13,7 @@ export type Props = { /** Control whether scroll can chain to parent scrollable containers */ overscrollBehavior?: "auto" | "contain"; /** Display a fade mask on the sides of the area that can be scrolled towards */ - fadeMask?: boolean; + fade?: boolean; /** Callback when the scroll area is scrolled */ onScroll?: (args: Coordinates) => void; /** Height of the scroll area, literal css value or unit token multiplier */ diff --git a/packages/reshaped/src/components/ScrollArea/tests/ScrollArea.stories.tsx b/packages/reshaped/src/components/ScrollArea/tests/ScrollArea.stories.tsx index c64d763f..5e81419c 100644 --- a/packages/reshaped/src/components/ScrollArea/tests/ScrollArea.stories.tsx +++ b/packages/reshaped/src/components/ScrollArea/tests/ScrollArea.stories.tsx @@ -154,12 +154,12 @@ export const overscrollBehavior = { ), }; -export const fadeMask = { - name: "fadeMask", +export const fade = { + name: "fade", render: () => ( - - + + Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown @@ -172,8 +172,8 @@ export const fadeMask = { - - + + Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s @@ -181,8 +181,8 @@ export const fadeMask = { - - + + Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown @@ -195,8 +195,8 @@ export const fadeMask = { - - + + Lorem Ipsum is simply dummy text of the printing and typesetting industry. From 6d192bedb2cbd06864f154fe0e6e278edf94807d Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 2 Jul 2026 21:02:42 +0000 Subject: [PATCH 4/6] refactor(ScrollArea): ease the fade mask gradient Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01VTo6CXkUBtLeftQaZ9RU4X --- .../components/ScrollArea/ScrollArea.module.css | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/packages/reshaped/src/components/ScrollArea/ScrollArea.module.css b/packages/reshaped/src/components/ScrollArea/ScrollArea.module.css index 6be3f3af..6ef6299e 100644 --- a/packages/reshaped/src/components/ScrollArea/ScrollArea.module.css +++ b/packages/reshaped/src/components/ScrollArea/ScrollArea.module.css @@ -101,15 +101,31 @@ linear-gradient( to bottom, rgb(0 0 0 / 0%) 0, + rgb(0 0 0 / 10%) calc(var(--rs-scroll-area-fade-block-start) * 0.2), + rgb(0 0 0 / 35%) calc(var(--rs-scroll-area-fade-block-start) * 0.4), + rgb(0 0 0 / 65%) calc(var(--rs-scroll-area-fade-block-start) * 0.6), + rgb(0 0 0 / 90%) calc(var(--rs-scroll-area-fade-block-start) * 0.8), rgb(0 0 0 / 100%) var(--rs-scroll-area-fade-block-start), rgb(0 0 0 / 100%) calc(100% - var(--rs-scroll-area-fade-block-end)), + rgb(0 0 0 / 90%) calc(100% - var(--rs-scroll-area-fade-block-end) * 0.8), + rgb(0 0 0 / 65%) calc(100% - var(--rs-scroll-area-fade-block-end) * 0.6), + rgb(0 0 0 / 35%) calc(100% - var(--rs-scroll-area-fade-block-end) * 0.4), + rgb(0 0 0 / 10%) calc(100% - var(--rs-scroll-area-fade-block-end) * 0.2), rgb(0 0 0 / 0%) 100% ), linear-gradient( var(--rs-scroll-area-fade-inline-direction), rgb(0 0 0 / 0%) 0, + rgb(0 0 0 / 10%) calc(var(--rs-scroll-area-fade-inline-start) * 0.2), + rgb(0 0 0 / 35%) calc(var(--rs-scroll-area-fade-inline-start) * 0.4), + rgb(0 0 0 / 65%) calc(var(--rs-scroll-area-fade-inline-start) * 0.6), + rgb(0 0 0 / 90%) calc(var(--rs-scroll-area-fade-inline-start) * 0.8), rgb(0 0 0 / 100%) var(--rs-scroll-area-fade-inline-start), rgb(0 0 0 / 100%) calc(100% - var(--rs-scroll-area-fade-inline-end)), + rgb(0 0 0 / 90%) calc(100% - var(--rs-scroll-area-fade-inline-end) * 0.8), + rgb(0 0 0 / 65%) calc(100% - var(--rs-scroll-area-fade-inline-end) * 0.6), + rgb(0 0 0 / 35%) calc(100% - var(--rs-scroll-area-fade-inline-end) * 0.4), + rgb(0 0 0 / 10%) calc(100% - var(--rs-scroll-area-fade-inline-end) * 0.2), rgb(0 0 0 / 0%) 100% ); mask-composite: intersect; From 310373a73b3d1b0804738e7392796c87e752c878 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 2 Jul 2026 21:19:30 +0000 Subject: [PATCH 5/6] refactor(Table): render scroll fade with scroll-driven animations Replaces the useFadeSide scroll tracking with the same CSS-only approach used by the ScrollArea fade prop, removing the scroll and resize listeners along with the JS RTL handling. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01VTo6CXkUBtLeftQaZ9RU4X --- .changeset/table-fade-scroll-driven.md | 5 ++ .../src/components/Table/Table.module.css | 74 ++++++++++++++----- .../reshaped/src/components/Table/Table.tsx | 9 +-- 3 files changed, 62 insertions(+), 26 deletions(-) create mode 100644 .changeset/table-fade-scroll-driven.md diff --git a/.changeset/table-fade-scroll-driven.md b/.changeset/table-fade-scroll-driven.md new file mode 100644 index 00000000..24f65bbb --- /dev/null +++ b/.changeset/table-fade-scroll-driven.md @@ -0,0 +1,5 @@ +--- +"reshaped": patch +--- + +Table: Fade mask for the horizontally scrollable content is now rendered with CSS scroll-driven animations instead of JS scroll tracking diff --git a/packages/reshaped/src/components/Table/Table.module.css b/packages/reshaped/src/components/Table/Table.module.css index 1d24c2f8..1291d50e 100644 --- a/packages/reshaped/src/components/Table/Table.module.css +++ b/packages/reshaped/src/components/Table/Table.module.css @@ -13,16 +13,62 @@ /* stylelint-enable plugin/browser-compat */ .root { + --rs-table-fade-size: var(--rs-unit-x4); + overflow: auto; - mask-image: linear-gradient( - to right, - rgb(0 0 0 / 0%) 0, - rgb(0 0 0 / 100%) var(--rs-table-fade-start), - rgb(0 0 0 / 100%) calc(100% - var(--rs-table-fade-end)), - rgb(0 0 0 / 0%) 100% - ); - transition: var(--rs-duration-fast) var(--rs-easing-decelerate); - transition-property: --rs-table-fade-start, --rs-table-fade-end; +} + +@keyframes fade-start { + from { + --rs-table-fade-start: 0px; + } + + to { + --rs-table-fade-start: var(--rs-table-fade-size); + } +} + +@keyframes fade-end { + from { + --rs-table-fade-end: var(--rs-table-fade-size); + } + + to { + --rs-table-fade-end: 0px; + } +} + +@supports (animation-timeline: scroll(self inline)) { + .root { + --rs-table-fade-direction: to right; + + mask-image: linear-gradient( + var(--rs-table-fade-direction), + rgb(0 0 0 / 0%) 0, + rgb(0 0 0 / 10%) calc(var(--rs-table-fade-start) * 0.2), + rgb(0 0 0 / 35%) calc(var(--rs-table-fade-start) * 0.4), + rgb(0 0 0 / 65%) calc(var(--rs-table-fade-start) * 0.6), + rgb(0 0 0 / 90%) calc(var(--rs-table-fade-start) * 0.8), + rgb(0 0 0 / 100%) var(--rs-table-fade-start), + rgb(0 0 0 / 100%) calc(100% - var(--rs-table-fade-end)), + rgb(0 0 0 / 90%) calc(100% - var(--rs-table-fade-end) * 0.8), + rgb(0 0 0 / 65%) calc(100% - var(--rs-table-fade-end) * 0.6), + rgb(0 0 0 / 35%) calc(100% - var(--rs-table-fade-end) * 0.4), + rgb(0 0 0 / 10%) calc(100% - var(--rs-table-fade-end) * 0.2), + rgb(0 0 0 / 0%) 100% + ); + animation-name: fade-start, fade-end; + animation-timing-function: linear; + animation-fill-mode: both; + animation-timeline: scroll(self inline), scroll(self inline); + animation-range: + 0 var(--rs-table-fade-size), + calc(100% - var(--rs-table-fade-size)) 100%; + } + + [dir="rtl"] .root { + --rs-table-fade-direction: to left; + } } .table { @@ -98,13 +144,3 @@ padding-inline-end: calc(var(--rs-unit-x1) * var(--rs-table-p-horizontal-s)); } } - -.--fade-start, -[dir="rtl"] .--fade-end { - --rs-table-fade-start: var(--rs-unit-x4); -} - -.--fade-end, -[dir="rtl"] .--fade-start { - --rs-table-fade-end: var(--rs-unit-x4); -} diff --git a/packages/reshaped/src/components/Table/Table.tsx b/packages/reshaped/src/components/Table/Table.tsx index 308bfe3e..b0c86853 100644 --- a/packages/reshaped/src/components/Table/Table.tsx +++ b/packages/reshaped/src/components/Table/Table.tsx @@ -3,7 +3,6 @@ import React, { isValidElement } from "react"; import { classNames } from "@reshaped/utilities"; -import useFadeSide from "@/hooks/_internal/useFadeSide"; import { responsiveVariables } from "@/utilities/props"; import { resolveMixin } from "@/styles/mixin"; import type * as T from "./Table.types"; @@ -100,15 +99,11 @@ export const TableHead: React.FC = (props) => { const Table: React.FC = (props) => { const { children, border, columnBorder, className, attributes } = props; - const rootRef = React.useRef(null); - const fadeSide = useFadeSide(rootRef); const rootClassNames = classNames( s.root, className, border && s["--border-outer"], - columnBorder && s["--border-column"], - (fadeSide === "start" || fadeSide === "both") && s["--fade-start"], - (fadeSide === "end" || fadeSide === "both") && s["--fade-end"] + columnBorder && s["--border-column"] ); const [firstChild] = React.Children.toArray(children); const isElement = isValidElement(firstChild); @@ -116,7 +111,7 @@ const Table: React.FC = (props) => { const isHead = isElement && firstChild.type === TableHead; return ( -
+
{isBody || isHead ? children : {children}}
From f6184f33048a302861c662dbf4889d5b5cf18cac Mon Sep 17 00:00:00 2001 From: Dmitry Belyaev Date: Thu, 2 Jul 2026 23:32:00 +0200 Subject: [PATCH 6/6] fade size --- .../reshaped/src/components/ScrollArea/ScrollArea.module.css | 2 +- packages/reshaped/src/components/Table/Table.module.css | 2 +- .../reshaped/src/components/Table/tests/Table.stories.tsx | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/reshaped/src/components/ScrollArea/ScrollArea.module.css b/packages/reshaped/src/components/ScrollArea/ScrollArea.module.css index 6ef6299e..288ca5e9 100644 --- a/packages/reshaped/src/components/ScrollArea/ScrollArea.module.css +++ b/packages/reshaped/src/components/ScrollArea/ScrollArea.module.css @@ -27,7 +27,7 @@ .root { --rs-scroll-area-thumb-size: var(--rs-unit-x1); --rs-scroll-area-thumb-offset: var(--rs-unit-x1); - --rs-scroll-area-fade-size: var(--rs-unit-x6); + --rs-scroll-area-fade-size: var(--rs-unit-x8); overflow: hidden; height: 100%; diff --git a/packages/reshaped/src/components/Table/Table.module.css b/packages/reshaped/src/components/Table/Table.module.css index 1291d50e..a6e0224c 100644 --- a/packages/reshaped/src/components/Table/Table.module.css +++ b/packages/reshaped/src/components/Table/Table.module.css @@ -13,7 +13,7 @@ /* stylelint-enable plugin/browser-compat */ .root { - --rs-table-fade-size: var(--rs-unit-x4); + --rs-table-fade-size: var(--rs-unit-x8); overflow: auto; } diff --git a/packages/reshaped/src/components/Table/tests/Table.stories.tsx b/packages/reshaped/src/components/Table/tests/Table.stories.tsx index 524fe7f1..0ec4a634 100644 --- a/packages/reshaped/src/components/Table/tests/Table.stories.tsx +++ b/packages/reshaped/src/components/Table/tests/Table.stories.tsx @@ -281,8 +281,8 @@ export const edgeCases = { - Column 1 - Column 2 + Column 1 + Column 2 Cell 1