Skip to content

Commit a741acd

Browse files
feat(Dialog): add custom scrollbar styling and new dialog story (#536)
* feat(Dialog): add custom scrollbar styling and new dialog story * feat(Scrollbar): add new story for scrollbar styles with customizable options * feat(version-plan): add version plan for custom scrollbar utility and update dependencies
1 parent 171688b commit a741acd

File tree

5 files changed

+125
-2
lines changed

5 files changed

+125
-2
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@ledgerhq/lumen-design-core': patch
3+
'@ledgerhq/lumen-ui-react': patch
4+
---
5+
6+
feat: add custom scrollbar utility: `scrollbar-custom` and use it in Dialog.

libs/design-core/src/utils/createCustomPlugin.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,30 @@ export function createScrollbarPlugin(): TailwindPlugin {
401401
display: 'none',
402402
},
403403
},
404+
'.scrollbar-custom': {
405+
'scrollbar-width': 'thin',
406+
'scrollbar-color': 'var(--background-muted-strong) transparent',
407+
'&::-webkit-scrollbar': {
408+
width: 'var(--size-10)',
409+
},
410+
'&::-webkit-scrollbar-track': {
411+
background: 'transparent',
412+
},
413+
'&::-webkit-scrollbar-thumb': {
414+
'background-color': 'var(--background-muted-strong)',
415+
'border-radius': 'var(--border-radius-full)',
416+
border: 'var(--size-2) solid transparent',
417+
'background-clip': 'padding-box',
418+
},
419+
'&::-webkit-scrollbar-thumb:hover': {
420+
'background-color': 'var(--background-muted-strong-hover)',
421+
border: 'var(--size-2) solid transparent',
422+
},
423+
'&::-webkit-scrollbar-thumb:active': {
424+
'background-color': 'var(--background-muted-strong-hover)',
425+
border: 'var(--size-2) solid transparent',
426+
},
427+
},
404428
});
405429
});
406430
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import type { Meta, StoryObj } from '@storybook/react-vite';
2+
import { SectionHeader } from '../shared';
3+
4+
const meta: Meta = {
5+
title: 'Foundations/Styles/Scrollbar',
6+
};
7+
8+
export default meta;
9+
type Story = StoryObj;
10+
11+
const PLACEHOLDER_ITEMS = Array.from({ length: 30 }, (_, i) => `Item ${i + 1}`);
12+
13+
const ScrollableBox = ({
14+
title,
15+
utilityClass,
16+
description,
17+
}: {
18+
title: string;
19+
utilityClass: string;
20+
description?: string;
21+
}) => (
22+
<div>
23+
<div
24+
className={`h-224 overflow-y-auto rounded-lg border border-muted-subtle bg-canvas p-16 ${utilityClass}`}
25+
>
26+
{PLACEHOLDER_ITEMS.map((item) => (
27+
<div
28+
key={item}
29+
className='border-b border-muted-subtle py-12 body-2 text-base'
30+
>
31+
{item}
32+
</div>
33+
))}
34+
</div>
35+
<div className='mt-8 body-2 text-base'>{title}</div>
36+
<div className='mt-4 body-4 text-muted'>
37+
<code>{utilityClass}</code> {description}
38+
</div>
39+
</div>
40+
);
41+
42+
export const Scrollbar: Story = {
43+
render: () => (
44+
<div className='p-24'>
45+
<SectionHeader
46+
title='Scrollbar'
47+
description='Tailwind utilities for controlling the scrollbar appearance of scrollable elements.'
48+
/>
49+
<div className='grid grid-cols-1 gap-40 lg:grid-cols-3'>
50+
<ScrollableBox
51+
title='Default'
52+
utilityClass=''
53+
description='Browser default scrollbar'
54+
/>
55+
<ScrollableBox title='Custom' utilityClass='scrollbar-custom' />
56+
<ScrollableBox title='None' utilityClass='scrollbar-none' />
57+
</div>
58+
</div>
59+
),
60+
};

libs/ui-react/src/lib/Components/Dialog/Dialog.stories.tsx

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
ListItemSpot,
1111
ListItemTitle,
1212
} from '../ListItem';
13-
import { SearchInput } from '../SearchInput/SearchInput';
13+
import { SearchInput } from '../SearchInput';
1414
import { Spot } from '../Spot';
1515
import { Tile, TileContent, TileSpot, TileTitle } from '../Tile';
1616
import {
@@ -600,6 +600,38 @@ export const WithListsContent: Story = {
600600
},
601601
};
602602

603+
export const WithScrollbar: Story = {
604+
render: () => (
605+
<Dialog height='fixed'>
606+
<DialogTrigger asChild>
607+
<Button appearance='base'>Open Dialog</Button>
608+
</DialogTrigger>
609+
<DialogContent>
610+
<DialogHeader
611+
appearance='compact'
612+
title='Custom Scrollbar'
613+
description='Fixed height with styled scrollbar'
614+
/>
615+
<DialogBody scrollbarWidth='auto'>
616+
<div className='-mx-8 flex flex-col gap-4'>
617+
{Array.from({ length: 10 }).map((_, i) => (
618+
<ListItem key={i}>
619+
<ListItemLeading>
620+
<ListItemSpot appearance='icon' icon={Chart1} />
621+
<ListItemContent>
622+
<ListItemTitle>Content item</ListItemTitle>
623+
<ListItemDescription>{`item ${i + 1}.`}</ListItemDescription>
624+
</ListItemContent>
625+
</ListItemLeading>
626+
</ListItem>
627+
))}
628+
</div>
629+
</DialogBody>
630+
</DialogContent>
631+
</Dialog>
632+
),
633+
};
634+
603635
export const InfoStateVariants: Story = {
604636
render: () => {
605637
return (

libs/ui-react/src/lib/Components/Dialog/Dialog.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,11 +235,12 @@ export const DialogBody = ({
235235
<div
236236
ref={ref}
237237
data-slot='dialog-body'
238-
style={{ scrollbarWidth, ...style }}
238+
style={style}
239239
className={cn(
240240
'-mb-24 flex min-h-0 grow flex-col overflow-y-auto px-24 pb-24',
241241
height === 'hug' ? 'basis-auto' : 'basis-0',
242242
className,
243+
scrollbarWidth === 'auto' ? 'scrollbar-custom' : 'scrollbar-none',
243244
)}
244245
{...props}
245246
>

0 commit comments

Comments
 (0)