From 669ab2fd97f6e540714282194978e0225f7e7489 Mon Sep 17 00:00:00 2001 From: Afonso Jorge Ramos Date: Thu, 30 Jul 2026 19:08:31 +0200 Subject: [PATCH] feat(ui): fade scrollable content at the top and bottom edges --- src/renderer/App.css | 54 +++++++++++++++++++ .../components/layout/Contents.test.tsx | 6 +++ src/renderer/components/layout/Contents.tsx | 3 ++ .../__snapshots__/Contents.test.tsx.snap | 10 ++++ .../LoginWithPersonalAccessTokenForm.tsx | 2 +- src/renderer/routes/AccountScopes.tsx | 2 +- src/renderer/routes/Accounts.tsx | 2 +- src/renderer/routes/Filters.tsx | 2 +- src/renderer/routes/Settings.tsx | 2 +- .../__snapshots__/Filters.test.tsx.snap | 2 +- .../__snapshots__/Settings.test.tsx.snap | 2 +- ...LoginWithPersonalAccessToken.test.tsx.snap | 2 +- ...LoginWithPersonalAccessToken.test.tsx.snap | 2 +- .../routes/github/LoginWithDeviceFlow.tsx | 2 +- .../routes/github/LoginWithOAuthApp.tsx | 2 +- .../LoginWithOAuthApp.test.tsx.snap | 2 +- ...LoginWithPersonalAccessToken.test.tsx.snap | 2 +- 17 files changed, 86 insertions(+), 13 deletions(-) diff --git a/src/renderer/App.css b/src/renderer/App.css index 6e486a595..9c96f7919 100644 --- a/src/renderer/App.css +++ b/src/renderer/App.css @@ -32,6 +32,60 @@ body, background-color: var(--gitify-scrollbar-thumb-hover); } +/** + * Softens the top and bottom edges of a scroll container, revealing each edge + * only while there is more content to scroll in that direction. + */ +@property --gitify-scroll-fade-top { + syntax: ''; + inherits: false; + initial-value: 0px; +} + +@property --gitify-scroll-fade-bottom { + syntax: ''; + inherits: false; + initial-value: 0px; +} + +@keyframes gitify-scroll-fade-top { + from { + --gitify-scroll-fade-top: 0px; + } + to { + --gitify-scroll-fade-top: var(--gitify-scroll-fade-size); + } +} + +@keyframes gitify-scroll-fade-bottom { + from { + --gitify-scroll-fade-bottom: var(--gitify-scroll-fade-size); + } + to { + --gitify-scroll-fade-bottom: 0px; + } +} + +.gitify-scroll-fade { + --gitify-scroll-fade-size: 1.5rem; + + mask-image: linear-gradient( + to bottom, + transparent, + black var(--gitify-scroll-fade-top), + black calc(100% - var(--gitify-scroll-fade-bottom)), + transparent + ); + + animation: + gitify-scroll-fade-top linear both, + gitify-scroll-fade-bottom linear both; + animation-timeline: scroll(self block); + animation-range: + 0px var(--gitify-scroll-fade-size), + calc(100% - var(--gitify-scroll-fade-size)) 100%; +} + body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', diff --git a/src/renderer/components/layout/Contents.test.tsx b/src/renderer/components/layout/Contents.test.tsx index c13251831..9eecc17aa 100644 --- a/src/renderer/components/layout/Contents.test.tsx +++ b/src/renderer/components/layout/Contents.test.tsx @@ -8,4 +8,10 @@ describe('renderer/components/layout/Contents.tsx', () => { expect(tree.container).toMatchSnapshot(); }); + + it('should render with scroll fade', () => { + const tree = renderWithProviders(Test); + + expect(tree.container).toMatchSnapshot(); + }); }); diff --git a/src/renderer/components/layout/Contents.tsx b/src/renderer/components/layout/Contents.tsx index af02c7520..c51ce6ffe 100644 --- a/src/renderer/components/layout/Contents.tsx +++ b/src/renderer/components/layout/Contents.tsx @@ -6,6 +6,7 @@ interface IContents { children: ReactNode; paddingHorizontal?: boolean; paddingBottom?: boolean; + scrollFade?: boolean; } /** @@ -16,6 +17,7 @@ export const Contents: FC = ({ children, paddingHorizontal = true, paddingBottom = false, + scrollFade = false, }) => { return (
= ({ 'grow overflow-x-hidden overflow-y-auto', paddingHorizontal && 'px-5', paddingBottom && 'pb-2', + scrollFade && 'gitify-scroll-fade', )} > {children} diff --git a/src/renderer/components/layout/__snapshots__/Contents.test.tsx.snap b/src/renderer/components/layout/__snapshots__/Contents.test.tsx.snap index 2b0270a32..c15ca421a 100644 --- a/src/renderer/components/layout/__snapshots__/Contents.test.tsx.snap +++ b/src/renderer/components/layout/__snapshots__/Contents.test.tsx.snap @@ -9,3 +9,13 @@ exports[`renderer/components/layout/Contents.tsx > should render itself & its ch
`; + +exports[`renderer/components/layout/Contents.tsx > should render with scroll fade 1`] = ` +
+
+ Test +
+
+`; diff --git a/src/renderer/components/login/LoginWithPersonalAccessTokenForm.tsx b/src/renderer/components/login/LoginWithPersonalAccessTokenForm.tsx index 38fbd2d10..00800b1ff 100644 --- a/src/renderer/components/login/LoginWithPersonalAccessTokenForm.tsx +++ b/src/renderer/components/login/LoginWithPersonalAccessTokenForm.tsx @@ -170,7 +170,7 @@ export const LoginWithPersonalAccessTokenForm: FC
{title}
- + {errors.invalidCredentialsForHost && ( { return (
Scopes
- + {!scopesLoaded && ( {
Accounts
- + {accounts.map((account, i) => { const adapter = getAdapter(account); const AuthMethodIcon = adapter.getAuthMethodIcon(account.method); diff --git a/src/renderer/routes/Filters.tsx b/src/renderer/routes/Filters.tsx index d42c64c75..7a7dca6ce 100644 --- a/src/renderer/routes/Filters.tsx +++ b/src/renderer/routes/Filters.tsx @@ -28,7 +28,7 @@ export const FiltersRoute: FC = () => { Filters - + {hasMultipleAccounts && } diff --git a/src/renderer/routes/Settings.tsx b/src/renderer/routes/Settings.tsx index 8944b6376..4a98bd4ba 100644 --- a/src/renderer/routes/Settings.tsx +++ b/src/renderer/routes/Settings.tsx @@ -20,7 +20,7 @@ export const SettingsRoute: FC = () => { Settings - + diff --git a/src/renderer/routes/__snapshots__/Filters.test.tsx.snap b/src/renderer/routes/__snapshots__/Filters.test.tsx.snap index dcab13d85..fe200e943 100644 --- a/src/renderer/routes/__snapshots__/Filters.test.tsx.snap +++ b/src/renderer/routes/__snapshots__/Filters.test.tsx.snap @@ -90,7 +90,7 @@ exports[`renderer/routes/Filters.tsx > General > should render itself & its chil
should render itself & its children 1`]
renders co
renders correc
{
Authorize with GitHub
- + {error && ( {
Login with OAuth App
- + {errors.invalidCredentialsForHost && ( renders correctly 1`] =
renders corre