aural-ui 4.0.1 → 4.2.0
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.
- package/README.md +8 -1
- package/dist/components/aspect-ratio/AspectRatio.stories.tsx +290 -1228
- package/dist/components/avatar/Avatar.stories.tsx +219 -235
- package/dist/components/badge/Badge.stories.tsx +379 -116
- package/dist/components/banner/Banner.stories.tsx +445 -391
- package/dist/components/breadcrumb/Breadcrumb.stories.tsx +453 -199
- package/dist/components/button/Button.stories.tsx +585 -230
- package/dist/components/card/Card.stories.tsx +619 -301
- package/dist/components/char-count/CharCount.stories.tsx +350 -248
- package/dist/components/checkbox/Checkbox.stories.tsx +309 -167
- package/dist/components/chip/Chip.stories.tsx +362 -168
- package/dist/components/circular-loader/CircularLoader.stories.tsx +221 -636
- package/dist/components/clamp-lines/ClampLines.stories.tsx +246 -117
- package/dist/components/collapsible/Collapsible.stories.tsx +391 -252
- package/dist/components/command/Command.stories.tsx +530 -867
- package/dist/components/dialog/Dialog.stories.tsx +501 -950
- package/dist/components/divider/Divider.stories.tsx +264 -527
- package/dist/components/dot-loader/DotLoader.stories.tsx +256 -257
- package/dist/components/drawer/Drawer.stories.tsx +659 -1023
- package/dist/components/dropdown/Dropdown.stories.tsx +643 -1028
- package/dist/components/form/Form.stories.tsx +560 -274
- package/dist/components/helper-text/HelperText.stories.tsx +199 -200
- package/dist/components/hover-card/HoverCard.stories.tsx +318 -1254
- package/dist/components/icon-button/IconButton.stories.tsx +837 -194
- package/dist/components/if-else/if-else.stories.tsx +370 -83
- package/dist/components/input/Input.stories.tsx +436 -368
- package/dist/components/label/Label.stories.tsx +156 -154
- package/dist/components/list/List.stories.tsx +484 -835
- package/dist/components/marquee/Marquee.stories.tsx +356 -712
- package/dist/components/otp-inputs/OtpInputs.stories.tsx +352 -422
- package/dist/components/overlay/Overlay.stories.tsx +452 -824
- package/dist/components/pagination/Pagination.stories.tsx +721 -210
- package/dist/components/popover/Popover.stories.tsx +481 -896
- package/dist/components/radio/Radio.stories.tsx +432 -124
- package/dist/components/resizable/Resizable.stories.tsx +495 -799
- package/dist/components/scroll-area/ScrollArea.stories.tsx +383 -1059
- package/dist/components/search/Search.stories.tsx +312 -595
- package/dist/components/select/Select.stories.tsx +684 -789
- package/dist/components/sheet/Sheet.stories.tsx +671 -950
- package/dist/components/skelton/Skelton.stories.tsx +230 -764
- package/dist/components/slider/Slider.stories.tsx +383 -760
- package/dist/components/stepper/Stepper.stories.tsx +371 -514
- package/dist/components/switch/Switch.stories.tsx +461 -208
- package/dist/components/switch-case/SwitchCase.stories.tsx +367 -188
- package/dist/components/table/Table.stories.tsx +770 -916
- package/dist/components/tabs/Tabs.stories.tsx +458 -1455
- package/dist/components/tag/Tag.stories.tsx +714 -542
- package/dist/components/textarea/TextArea.stories.tsx +621 -562
- package/dist/components/thumbnail-tags/ThumbnailTags.stories.tsx +228 -154
- package/dist/components/toast/Toast.stories.tsx +452 -1339
- package/dist/components/toggle/Toggle.stories.tsx +488 -931
- package/dist/components/tooltip/Tooltip.stories.tsx +344 -1388
- package/dist/components/typography/Typography.stories.tsx +406 -89
- package/dist/hooks/use-change-state/UseChangeState.stories.tsx +309 -606
- package/dist/hooks/use-previous/UsePrevious.stories.tsx +367 -917
- package/dist/hooks/use-standalone-pagination/UseStandalonePagination.stories.tsx +639 -867
- package/dist/icons/Icons.stories.tsx +0 -12
- package/dist/icons/ai-avatar-icon/AiAvatarIcon.stories.tsx +223 -1060
- package/dist/icons/alert-icon/AlertIcon.stories.tsx +106 -968
- package/dist/icons/all-icons.tsx +37 -16
- package/dist/icons/angle-down-icon/AngleDownIcon.stories.tsx +137 -1010
- package/dist/icons/apple-logo-icon/AppleLogoIcon.stories.tsx +145 -935
- package/dist/icons/arrow-box-left-icon/ArrowBoxLeftIcon.stories.tsx +132 -1046
- package/dist/icons/arrow-corner-up-left-icon/ArrowCornerUpLeftIcon.stories.tsx +134 -986
- package/dist/icons/arrow-corner-up-right-icon/ArrowCornerUpRightIcon.stories.tsx +135 -1028
- package/dist/icons/arrow-left-icon/ArrowLeftIcon.stories.tsx +133 -971
- package/dist/icons/arrow-right-icon/ArrowRightIcon.stories.tsx +145 -1123
- package/dist/icons/arrow-right-up-icon/ArrowRightUpIcon.stories.tsx +143 -1252
- package/dist/icons/art-board-icon/ArtBoardIcon.stories.tsx +123 -632
- package/dist/icons/audio-bar-icon/AudioBarIcon.stories.tsx +141 -1223
- package/dist/icons/backward-ten-seconds-icon/BackwardTenSecondsIcon.stories.tsx +164 -1018
- package/dist/icons/bubble-check-icon/BubbleCheckIcon.stories.tsx +121 -1236
- package/dist/icons/bubble-crossed-icon/BubbleCrossedIcon.stories.tsx +121 -1213
- package/dist/icons/bubble-sparkle-icon/BubbleSparkleIcon.stories.tsx +116 -893
- package/dist/icons/camera-icon/CameraIcon.stories.tsx +109 -1254
- package/dist/icons/capital-a-letter-icon/CapitalALetterIcon.stories.tsx +114 -975
- package/dist/icons/chevron-double-left-icon/ChevronDoubleLeftIcon.stories.tsx +157 -994
- package/dist/icons/chevron-double-right-icon/ChevronDoubleRightIcon.stories.tsx +160 -992
- package/dist/icons/chevron-down-icon/ChevronDownIcon.stories.tsx +140 -970
- package/dist/icons/chevron-left-icon/ChevronLeftIcon.stories.tsx +126 -993
- package/dist/icons/chevron-right-icon/ChevronRightIcon.stories.tsx +144 -987
- package/dist/icons/chevron-up-icon/ChevronUpIcon.stories.tsx +141 -1007
- package/dist/icons/circle-tick-icon/CircleTickIcon.stories.tsx +147 -1187
- package/dist/icons/circular-play-icon/CircularPlayIcon.stories.tsx +110 -476
- package/dist/icons/coin-icon/CoinIcon.stories.tsx +120 -1364
- package/dist/icons/coin-toons-icon/CoinToonsIcon.stories.tsx +113 -1360
- package/dist/icons/column-wide-add-icon/ColumnWideAddIcon.stories.tsx +111 -942
- package/dist/icons/command-icon/CommandIcon.stories.tsx +124 -1087
- package/dist/icons/copy-icon/CopyIcon.stories.tsx +119 -996
- package/dist/icons/cross-circle-icon/CrossCircleIcon.stories.tsx +144 -1046
- package/dist/icons/cross-icon/CrossIcon.stories.tsx +136 -999
- package/dist/icons/download-icon/DownloadIcon.stories.tsx +123 -857
- package/dist/icons/edit-big-icon/EditBigIcon.stories.tsx +121 -1080
- package/dist/icons/email-icon/EmailIcon.stories.tsx +112 -979
- package/dist/icons/expand-icon/ExpandIcon.stories.tsx +109 -1146
- package/dist/icons/eye-close-icon/EyeCloseIcon.stories.tsx +141 -1068
- package/dist/icons/eye-open-icon/EyeOpenIcon.stories.tsx +140 -1081
- package/dist/icons/feature-shine-icon/FeatureShineIcon.stories.tsx +124 -1050
- package/dist/icons/file-chart-icon/FileChartIcon.stories.tsx +123 -1091
- package/dist/icons/file-text-icon/FileTextIcon.stories.tsx +122 -633
- package/dist/icons/filter-bar-row-icon/FilterBarRowIcon.stories.tsx +116 -1087
- package/dist/icons/forward-ten-seconds-icon/ForwardTenSecondsIcon.stories.tsx +166 -1020
- package/dist/icons/git-branch-icon/GitBranchIcon.stories.tsx +112 -1182
- package/dist/icons/git-fork-icon/GitForkIcon.stories.tsx +112 -1155
- package/dist/icons/globe-icon/GlobeIcon.stories.tsx +127 -325
- package/dist/icons/google-logo-icon/GoogleLogoIcon.stories.tsx +142 -985
- package/dist/icons/grip-vertical-icon/GripVerticalIcon.stories.tsx +116 -1217
- package/dist/icons/head-icon/HeadIcon.stories.tsx +108 -953
- package/dist/icons/heart-icon/HeartIcon.stories.tsx +117 -1060
- package/dist/icons/image-avatar-sparkle-icon/ImageAvatarSparkleIcon.stories.tsx +116 -716
- package/dist/icons/image-icon/ImageIcon.stories.tsx +102 -1164
- package/dist/icons/import-folder-icon/ImportFolderIcon.stories.tsx +108 -1233
- package/dist/icons/import-left-arrow-folder-icon/ImportLeftArrowFolderIcon.stories.tsx +133 -1289
- package/dist/icons/indian-flag-icon/IndianFlagIcon.stories.tsx +155 -1012
- package/dist/icons/instagram-icon/InstagramIcon.stories.tsx +158 -1438
- package/dist/icons/layout-column-icon/LayoutColumnIcon.stories.tsx +121 -1011
- package/dist/icons/layout-left-icon/LayoutLeftIcon.stories.tsx +116 -981
- package/dist/icons/layout-right-icon/LayoutRightIcon.stories.tsx +116 -979
- package/dist/icons/light-bulb-simple-icon/LightBulbSimpleIcon.stories.tsx +105 -1252
- package/dist/icons/linked-in-icon/LinkedInIcon.stories.tsx +151 -1554
- package/dist/icons/magic-book-icon/MagicBookIcon.stories.tsx +107 -1227
- package/dist/icons/magic-edit-icon/MagicEditIcon.stories.tsx +116 -707
- package/dist/icons/maintenance-icon/MaintenanceIcon.stories.tsx +119 -1226
- package/dist/icons/message-icon/MessageIcon.stories.tsx +111 -557
- package/dist/icons/minimize-icon/MinimizeIcon.stories.tsx +112 -1198
- package/dist/icons/moon-icon/MoonIcon.stories.tsx +117 -557
- package/dist/icons/move-horizontal-icon/MoveHorizontalIcon.stories.tsx +106 -1235
- package/dist/icons/move-vertical-icon/MoveVerticalIcon.stories.tsx +112 -1185
- package/dist/icons/musical-note-icon/MusicalNoteIcon.stories.tsx +116 -1012
- package/dist/icons/notepad-icon/NotepadIcon.stories.tsx +108 -1137
- package/dist/icons/notes-icon/NotesIcon.stories.tsx +116 -1138
- package/dist/icons/page-search-icon/PageSearchIcon.stories.tsx +106 -1146
- package/dist/icons/page-text-icon/PageTextIcon.stories.tsx +119 -719
- package/dist/icons/paint-roll-icon/PaintRollIcon.stories.tsx +110 -999
- package/dist/icons/paper-plane-icon/PaperPlaneIcon.stories.tsx +109 -912
- package/dist/icons/pause-icon/PauseIcon.stories.tsx +110 -1041
- package/dist/icons/pencil-icon/PencilIcon.stories.tsx +112 -1109
- package/dist/icons/phone-icon/PhoneIcon.stories.tsx +112 -1023
- package/dist/icons/plus-icon/PlusIcon.stories.tsx +103 -1132
- package/dist/icons/pocket-studio-icon/PocketStudioIcon.stories.tsx +104 -870
- package/dist/icons/scroll-down-icon/ScrollDownIcon.stories.tsx +99 -476
- package/dist/icons/search-icon/SearchIcon.stories.tsx +108 -1161
- package/dist/icons/setting-icon/SettingIcon.stories.tsx +104 -1009
- package/dist/icons/share-icon/ShareIcon.stories.tsx +117 -1064
- package/dist/icons/shield-icon/ShieldIcon.stories.tsx +114 -974
- package/dist/icons/site-logo-icon/SiteLogoIcon.stories.tsx +134 -1160
- package/dist/icons/skip-backward-icon/SkipBackwardIcon.stories.tsx +169 -1017
- package/dist/icons/skip-forward-icon/SkipForwardIcon.stories.tsx +161 -1016
- package/dist/icons/sparkles-soft-icon/SparklesSoftIcon.stories.tsx +102 -1001
- package/dist/icons/spinner-gradient-icon/SpinnerGradientIcon.stories.tsx +155 -593
- package/dist/icons/spinner-solid-icon/SpinnerSolidIcon.stories.tsx +155 -608
- package/dist/icons/spinner-solid-neutral-icon/SpinnerSolidINeutralcon.stories.tsx +142 -712
- package/dist/icons/star-icon/StarIcon.stories.tsx +120 -946
- package/dist/icons/store-coin-icon/StoreCoinIcon.stories.tsx +109 -1013
- package/dist/icons/suggestion-icon/SuggestionIcon.stories.tsx +113 -891
- package/dist/icons/sun-icon/SunIcon.stories.tsx +117 -864
- package/dist/icons/text-color-icon/TextColorIcon.stories.tsx +113 -989
- package/dist/icons/text-indicator-icon/TextIndicatorIcon.stories.tsx +120 -1027
- package/dist/icons/threads-icon/ThreadsIcon.stories.tsx +153 -1476
- package/dist/icons/tick-circle-icon/TickCircleIcon.stories.tsx +143 -1187
- package/dist/icons/tick-icon/TickIcon.stories.tsx +142 -1322
- package/dist/icons/trash-icon/TrashIcon.stories.tsx +105 -970
- package/dist/icons/twitter-x-icon/TwitterXIcon.stories.tsx +154 -1457
- package/dist/icons/upload-icon/UploadIcon.stories.tsx +112 -930
- package/dist/icons/vertical-menu-icon/VerticalMenuIcon.stories.tsx +115 -1019
- package/dist/icons/video-play-list-icon/VideoPlaylistIcon.stories.tsx +122 -1092
- package/dist/icons/voice-playing-icon/VoicePlayingIcon.stories.tsx +120 -1401
- package/dist/icons/volume-full-icon/VolumeFullIcon.stories.tsx +107 -1212
- package/dist/icons/volume-half-icon/VolumeHalfIcon.stories.tsx +109 -1122
- package/dist/icons/volume-off-icon/VolumeOffIcon.stories.tsx +112 -1124
- package/dist/icons/warning-icon/WarningIcon.stories.tsx +119 -1083
- package/dist/icons/youtube-icon/YoutubeIcon.stories.tsx +158 -983
- package/dist/index.cjs +90 -90
- package/dist/index.js +90 -90
- package/package.json +8 -3
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import React, { useState } from "react"
|
|
2
2
|
import { Button } from "@components/button"
|
|
3
|
-
import { AlertIcon } from "@icons/alert-icon"
|
|
4
|
-
import { ChevronRightIcon } from "@icons/chevron-right-icon"
|
|
5
3
|
import { CrossIcon } from "@icons/cross-icon"
|
|
6
|
-
import { MaintenanceIcon } from "@icons/maintenance-icon"
|
|
7
4
|
import { SearchIcon } from "@icons/search-icon"
|
|
8
|
-
import { TickCircleIcon } from "@icons/tick-circle-icon"
|
|
9
5
|
import type { Meta, StoryObj } from "@storybook/react-vite"
|
|
10
6
|
|
|
7
|
+
import { AuralComponentDocsPage } from "src/ui/story-spec/components/component-story-docs-page"
|
|
8
|
+
|
|
11
9
|
import { Overlay } from "."
|
|
12
10
|
|
|
13
11
|
const meta: Meta<typeof Overlay> = {
|
|
@@ -15,74 +13,29 @@ const meta: Meta<typeof Overlay> = {
|
|
|
15
13
|
component: Overlay,
|
|
16
14
|
parameters: {
|
|
17
15
|
layout: "fullscreen",
|
|
18
|
-
backgrounds: {
|
|
19
|
-
default: "dark",
|
|
20
|
-
values: [
|
|
21
|
-
{ name: "dark", value: "#0a0a0a" },
|
|
22
|
-
{ name: "light", value: "#ffffff" },
|
|
23
|
-
],
|
|
24
|
-
},
|
|
25
16
|
docs: {
|
|
26
17
|
description: {
|
|
27
|
-
component:
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
A flexible overlay component for creating modal backgrounds, loading states, and layered content with customizable opacity, glass effects, and noise textures.
|
|
31
|
-
|
|
32
|
-
## Features
|
|
33
|
-
|
|
34
|
-
- **Multiple Opacity Levels**: High (80%), medium (60%), low (40%), or none
|
|
35
|
-
- **Glass Effect**: Backdrop blur with high, medium, low, or no blur
|
|
36
|
-
- **Noise Texture**: Visual noise patterns for enhanced aesthetics
|
|
37
|
-
- **Animation Support**: Built-in fade in/out animations
|
|
38
|
-
- **Z-Index Management**: Proper layering with configurable z-index
|
|
39
|
-
- **Content Positioning**: Automatic centering of overlay content
|
|
40
|
-
- **Event Handling**: Pointer events management for interactive overlays
|
|
41
|
-
|
|
42
|
-
## Variant Options
|
|
43
|
-
|
|
44
|
-
### Opacity Variants
|
|
45
|
-
- **high**: 80% black background - Maximum dimming
|
|
46
|
-
- **medium**: 60% black background - Balanced dimming (default)
|
|
47
|
-
- **low**: 40% black background - Subtle dimming
|
|
48
|
-
- **none**: Solid black background - Complete coverage
|
|
49
|
-
|
|
50
|
-
### Glass Effect Variants
|
|
51
|
-
- **high**: Strong backdrop blur - Heavy glass effect
|
|
52
|
-
- **medium**: Medium backdrop blur - Moderate glass effect
|
|
53
|
-
- **low**: Light backdrop blur - Subtle glass effect (default)
|
|
54
|
-
- **none**: No backdrop blur - No glass effect
|
|
55
|
-
|
|
56
|
-
### Noise Variants
|
|
57
|
-
- **high**: Strong noise texture - Pronounced texture
|
|
58
|
-
- **medium**: Medium noise texture - Balanced texture
|
|
59
|
-
- **low**: Light noise texture - Subtle texture (default)
|
|
60
|
-
- **none**: No noise texture - Clean overlay
|
|
61
|
-
|
|
62
|
-
## Usage Examples
|
|
63
|
-
|
|
64
|
-
### Basic Overlay
|
|
65
|
-
\`\`\`tsx
|
|
66
|
-
<Overlay>
|
|
67
|
-
<div>Overlay content</div>
|
|
68
|
-
</Overlay>
|
|
69
|
-
\`\`\`
|
|
70
|
-
|
|
71
|
-
### Custom Configuration
|
|
72
|
-
\`\`\`tsx
|
|
73
|
-
<Overlay opacity="high" glass="medium" noise="low">
|
|
74
|
-
<div className="bg-white/10 p-4 rounded-lg">Modal content</div>
|
|
75
|
-
</Overlay>
|
|
76
|
-
\`\`\`
|
|
77
|
-
|
|
78
|
-
### Loading State
|
|
79
|
-
\`\`\`tsx
|
|
80
|
-
<Overlay opacity="medium" glass="high">
|
|
81
|
-
<div className="text-fm-primary">Loading...</div>
|
|
82
|
-
</Overlay>
|
|
83
|
-
\`\`\`
|
|
84
|
-
`,
|
|
18
|
+
component:
|
|
19
|
+
"A flexible full-screen overlay for modal backgrounds, loading states, and layered UI. Offers independent control over opacity (low/medium/high/none), glass blur (none/low/medium/high), and noise texture (none/low/medium/high). Renders children in a centered z-50 container above the scrim.",
|
|
85
20
|
},
|
|
21
|
+
page: () => (
|
|
22
|
+
<AuralComponentDocsPage
|
|
23
|
+
features={[
|
|
24
|
+
{
|
|
25
|
+
title: "Opacity Control",
|
|
26
|
+
description: "Low to high scrim",
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
title: "Glass Blur",
|
|
30
|
+
description: "None to high backdrop",
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
title: "Noise Texture",
|
|
34
|
+
description: "Grain overlay level",
|
|
35
|
+
},
|
|
36
|
+
]}
|
|
37
|
+
/>
|
|
38
|
+
),
|
|
86
39
|
},
|
|
87
40
|
},
|
|
88
41
|
tags: ["autodocs"],
|
|
@@ -91,48 +44,48 @@ A flexible overlay component for creating modal backgrounds, loading states, and
|
|
|
91
44
|
export default meta
|
|
92
45
|
type Story = StoryObj<typeof Overlay>
|
|
93
46
|
|
|
94
|
-
//
|
|
95
|
-
|
|
96
|
-
|
|
47
|
+
// ─── shared backing content ───────────────────────────────────────────────────
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* A content card background built entirely from design tokens.
|
|
51
|
+
* No hardcoded gradient colors.
|
|
52
|
+
*/
|
|
53
|
+
const BackingContent = () => (
|
|
54
|
+
<div className="bg-fm-surface-primary min-h-screen w-full p-8">
|
|
97
55
|
<div className="mx-auto max-w-4xl space-y-8">
|
|
98
|
-
<header className="
|
|
99
|
-
<
|
|
100
|
-
|
|
56
|
+
<header className="space-y-2">
|
|
57
|
+
<p className="text-fm-tertiary font-fm-brand text-fm-sm leading-fm-sm tracking-widest uppercase">
|
|
58
|
+
PocketFM
|
|
59
|
+
</p>
|
|
60
|
+
<h1 className="text-fm-primary font-fm-brand text-fm-4xl leading-fm-4xl font-semibold">
|
|
61
|
+
Your music, everywhere
|
|
101
62
|
</h1>
|
|
102
|
-
<p className="text-fm-
|
|
103
|
-
|
|
63
|
+
<p className="text-fm-secondary font-fm-text text-fm-lg leading-fm-xl">
|
|
64
|
+
Discover curated playlists, follow your favourite artists, and listen
|
|
65
|
+
offline — all in one place.
|
|
104
66
|
</p>
|
|
105
67
|
</header>
|
|
106
68
|
|
|
107
69
|
<div className="grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3">
|
|
108
|
-
{
|
|
70
|
+
{[
|
|
71
|
+
{ label: "New Releases", meta: "Updated today" },
|
|
72
|
+
{ label: "Top Charts", meta: "Global & local" },
|
|
73
|
+
{ label: "Your Mixes", meta: "Personalised for you" },
|
|
74
|
+
{ label: "Artist Radio", meta: "Endless discovery" },
|
|
75
|
+
{ label: "Podcasts", meta: "500 k+ shows" },
|
|
76
|
+
{ label: "Audiobooks", meta: "10 k+ titles" },
|
|
77
|
+
].map((card) => (
|
|
109
78
|
<div
|
|
110
|
-
key={
|
|
111
|
-
className="border-fm-divider-secondary bg-fm-surface-secondary
|
|
79
|
+
key={card.label}
|
|
80
|
+
className="border-fm-divider-secondary bg-fm-surface-secondary rounded-xl border p-6"
|
|
112
81
|
>
|
|
113
|
-
<div className="mb-4"
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
<div className="flex h-32 items-center justify-center rounded-lg bg-gradient-to-r from-cyan-500 to-blue-600">
|
|
121
|
-
<span className="font-medium text-white">
|
|
122
|
-
Image Placeholder
|
|
123
|
-
</span>
|
|
124
|
-
</div>
|
|
125
|
-
<p className="text-fm-tertiary text-sm">
|
|
126
|
-
This is some sample content that demonstrates how the overlay
|
|
127
|
-
affects background visibility and readability.
|
|
128
|
-
</p>
|
|
129
|
-
<div className="flex gap-2">
|
|
130
|
-
<Button size="sm" variant="outline">
|
|
131
|
-
Action
|
|
132
|
-
</Button>
|
|
133
|
-
<Button size="sm">Primary</Button>
|
|
134
|
-
</div>
|
|
135
|
-
</div>
|
|
82
|
+
<div className="bg-fm-surface-contrast mb-4 h-24 w-full rounded-lg" />
|
|
83
|
+
<p className="text-fm-primary font-fm-brand text-fm-md leading-fm-md font-semibold">
|
|
84
|
+
{card.label}
|
|
85
|
+
</p>
|
|
86
|
+
<p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm">
|
|
87
|
+
{card.meta}
|
|
88
|
+
</p>
|
|
136
89
|
</div>
|
|
137
90
|
))}
|
|
138
91
|
</div>
|
|
@@ -140,666 +93,203 @@ const BackgroundContent = () => (
|
|
|
140
93
|
</div>
|
|
141
94
|
)
|
|
142
95
|
|
|
143
|
-
// 1.
|
|
144
|
-
export const OpacityVariants: Story = {
|
|
145
|
-
render: () => {
|
|
146
|
-
const [activeOverlay, setActiveOverlay] = useState<string | null>(null)
|
|
96
|
+
// ─── 1. Configurations ────────────────────────────────────────────────────────
|
|
147
97
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
className="w-full"
|
|
166
|
-
>
|
|
167
|
-
Low (40%)
|
|
168
|
-
</Button>
|
|
169
|
-
<Button
|
|
170
|
-
size="sm"
|
|
171
|
-
variant={activeOverlay === "medium" ? "primary" : "outline"}
|
|
172
|
-
onClick={() =>
|
|
173
|
-
setActiveOverlay(activeOverlay === "medium" ? null : "medium")
|
|
174
|
-
}
|
|
175
|
-
className="w-full"
|
|
176
|
-
>
|
|
177
|
-
Medium (60%)
|
|
178
|
-
</Button>
|
|
179
|
-
<Button
|
|
180
|
-
size="sm"
|
|
181
|
-
variant={activeOverlay === "high" ? "primary" : "outline"}
|
|
182
|
-
onClick={() =>
|
|
183
|
-
setActiveOverlay(activeOverlay === "high" ? null : "high")
|
|
184
|
-
}
|
|
185
|
-
className="w-full"
|
|
186
|
-
>
|
|
187
|
-
High (80%)
|
|
188
|
-
</Button>
|
|
189
|
-
<Button
|
|
190
|
-
size="sm"
|
|
191
|
-
variant={activeOverlay === "none" ? "primary" : "outline"}
|
|
192
|
-
onClick={() =>
|
|
193
|
-
setActiveOverlay(activeOverlay === "none" ? null : "none")
|
|
194
|
-
}
|
|
195
|
-
className="w-full"
|
|
196
|
-
>
|
|
197
|
-
None (100%)
|
|
198
|
-
</Button>
|
|
199
|
-
</div>
|
|
200
|
-
{activeOverlay && (
|
|
201
|
-
<Button
|
|
202
|
-
size="sm"
|
|
203
|
-
variant="secondary"
|
|
204
|
-
onClick={() => setActiveOverlay(null)}
|
|
205
|
-
className="w-full"
|
|
206
|
-
>
|
|
207
|
-
Clear Overlay
|
|
208
|
-
</Button>
|
|
209
|
-
)}
|
|
210
|
-
</div>
|
|
211
|
-
</div>
|
|
98
|
+
export const Configurations: Story = {
|
|
99
|
+
render: () => {
|
|
100
|
+
const [activeOpacity, setActiveOpacity] = useState<
|
|
101
|
+
"low" | "medium" | "high" | "none" | null
|
|
102
|
+
>(null)
|
|
103
|
+
const [activeGlass, setActiveGlass] = useState<
|
|
104
|
+
"none" | "low" | "medium" | "high" | null
|
|
105
|
+
>(null)
|
|
106
|
+
const [activeNoise, setActiveNoise] = useState<
|
|
107
|
+
"none" | "low" | "medium" | "high" | null
|
|
108
|
+
>(null)
|
|
109
|
+
|
|
110
|
+
const clearAll = () => {
|
|
111
|
+
setActiveOpacity(null)
|
|
112
|
+
setActiveGlass(null)
|
|
113
|
+
setActiveNoise(null)
|
|
114
|
+
}
|
|
212
115
|
|
|
213
|
-
|
|
214
|
-
{activeOverlay === "low" && (
|
|
215
|
-
<Overlay opacity="low">
|
|
216
|
-
<div className="border-fm-divider-secondary bg-fm-surface-secondary/80 max-w-md rounded-lg border p-6 backdrop-blur-sm">
|
|
217
|
-
<h3 className="text-fm-primary mb-2 text-lg font-semibold">
|
|
218
|
-
Low Opacity Overlay
|
|
219
|
-
</h3>
|
|
220
|
-
<p className="text-fm-secondary text-sm">
|
|
221
|
-
40% background dimming - Background remains quite visible
|
|
222
|
-
</p>
|
|
223
|
-
</div>
|
|
224
|
-
</Overlay>
|
|
225
|
-
)}
|
|
116
|
+
const hasActive = activeOpacity || activeGlass || activeNoise
|
|
226
117
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
<p className="text-fm-secondary text-sm">
|
|
234
|
-
60% background dimming - Balanced visibility
|
|
235
|
-
</p>
|
|
236
|
-
</div>
|
|
237
|
-
</Overlay>
|
|
238
|
-
)}
|
|
239
|
-
|
|
240
|
-
{activeOverlay === "high" && (
|
|
241
|
-
<Overlay opacity="high">
|
|
242
|
-
<div className="border-fm-divider-secondary bg-fm-surface-secondary/80 max-w-md rounded-lg border p-6 backdrop-blur-sm">
|
|
243
|
-
<h3 className="text-fm-primary mb-2 text-lg font-semibold">
|
|
244
|
-
High Opacity Overlay
|
|
245
|
-
</h3>
|
|
246
|
-
<p className="text-fm-secondary text-sm">
|
|
247
|
-
80% background dimming - Strong focus on overlay content
|
|
248
|
-
</p>
|
|
249
|
-
</div>
|
|
250
|
-
</Overlay>
|
|
251
|
-
)}
|
|
118
|
+
const opacityOptions = [
|
|
119
|
+
{ value: "low", label: "Low · 40%" },
|
|
120
|
+
{ value: "medium", label: "Medium · 60%" },
|
|
121
|
+
{ value: "high", label: "High · 80%" },
|
|
122
|
+
{ value: "none", label: "Solid · 100%" },
|
|
123
|
+
] as const
|
|
252
124
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
<p className="text-fm-secondary text-sm">
|
|
260
|
-
100% background coverage - Complete background blocking
|
|
261
|
-
</p>
|
|
262
|
-
</div>
|
|
263
|
-
</Overlay>
|
|
264
|
-
)}
|
|
265
|
-
</div>
|
|
266
|
-
)
|
|
267
|
-
},
|
|
268
|
-
parameters: {
|
|
269
|
-
docs: {
|
|
270
|
-
description: {
|
|
271
|
-
story:
|
|
272
|
-
"Interactive demonstration of different opacity levels showing how background visibility changes from 40% to 100% coverage.",
|
|
273
|
-
},
|
|
274
|
-
},
|
|
275
|
-
},
|
|
276
|
-
}
|
|
125
|
+
const glassOptions = [
|
|
126
|
+
{ value: "none", label: "No blur" },
|
|
127
|
+
{ value: "low", label: "Low blur" },
|
|
128
|
+
{ value: "medium", label: "Medium blur" },
|
|
129
|
+
{ value: "high", label: "High blur" },
|
|
130
|
+
] as const
|
|
277
131
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
132
|
+
const noiseOptions = [
|
|
133
|
+
{ value: "none", label: "No texture" },
|
|
134
|
+
{ value: "low", label: "Low texture" },
|
|
135
|
+
{ value: "medium", label: "Medium texture" },
|
|
136
|
+
{ value: "high", label: "Strong texture" },
|
|
137
|
+
] as const
|
|
282
138
|
|
|
283
139
|
return (
|
|
284
|
-
<div className="
|
|
285
|
-
<
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
Glass Effect Variants
|
|
292
|
-
</h3>
|
|
293
|
-
<div className="space-y-2">
|
|
294
|
-
<Button
|
|
295
|
-
size="sm"
|
|
296
|
-
variant={activeGlass === "none" ? "primary" : "outline"}
|
|
297
|
-
onClick={() =>
|
|
298
|
-
setActiveGlass(activeGlass === "none" ? null : "none")
|
|
299
|
-
}
|
|
300
|
-
className="w-full"
|
|
301
|
-
>
|
|
302
|
-
No Glass
|
|
303
|
-
</Button>
|
|
304
|
-
<Button
|
|
305
|
-
size="sm"
|
|
306
|
-
variant={activeGlass === "low" ? "primary" : "outline"}
|
|
307
|
-
onClick={() =>
|
|
308
|
-
setActiveGlass(activeGlass === "low" ? null : "low")
|
|
309
|
-
}
|
|
310
|
-
className="w-full"
|
|
311
|
-
>
|
|
312
|
-
Low Blur
|
|
313
|
-
</Button>
|
|
314
|
-
<Button
|
|
315
|
-
size="sm"
|
|
316
|
-
variant={activeGlass === "medium" ? "primary" : "outline"}
|
|
317
|
-
onClick={() =>
|
|
318
|
-
setActiveGlass(activeGlass === "medium" ? null : "medium")
|
|
319
|
-
}
|
|
320
|
-
className="w-full"
|
|
321
|
-
>
|
|
322
|
-
Medium Blur
|
|
323
|
-
</Button>
|
|
324
|
-
<Button
|
|
325
|
-
size="sm"
|
|
326
|
-
variant={activeGlass === "high" ? "primary" : "outline"}
|
|
327
|
-
onClick={() =>
|
|
328
|
-
setActiveGlass(activeGlass === "high" ? null : "high")
|
|
329
|
-
}
|
|
330
|
-
className="w-full"
|
|
331
|
-
>
|
|
332
|
-
High Blur
|
|
333
|
-
</Button>
|
|
334
|
-
</div>
|
|
335
|
-
{activeGlass && (
|
|
336
|
-
<Button
|
|
337
|
-
size="sm"
|
|
338
|
-
variant="secondary"
|
|
339
|
-
onClick={() => setActiveGlass(null)}
|
|
340
|
-
className="w-full"
|
|
341
|
-
>
|
|
342
|
-
Clear Overlay
|
|
343
|
-
</Button>
|
|
344
|
-
)}
|
|
345
|
-
</div>
|
|
346
|
-
</div>
|
|
347
|
-
|
|
348
|
-
{/* Glass Effect Overlays */}
|
|
349
|
-
{activeGlass === "none" && (
|
|
350
|
-
<Overlay opacity="medium" glass="none">
|
|
351
|
-
<div className="border-fm-divider-secondary bg-fm-surface-secondary/80 max-w-md rounded-lg border p-6">
|
|
352
|
-
<h3 className="text-fm-primary mb-2 text-lg font-semibold">
|
|
353
|
-
No Glass Effect
|
|
354
|
-
</h3>
|
|
355
|
-
<p className="text-fm-secondary text-sm">
|
|
356
|
-
Clean overlay without backdrop blur
|
|
140
|
+
<div className="w-full p-8">
|
|
141
|
+
<div className="mx-auto grid max-w-5xl grid-cols-1 gap-6 lg:grid-cols-[280px_minmax(0,1fr)]">
|
|
142
|
+
{/* ── Control panel ── */}
|
|
143
|
+
<div className="border-fm-divider-secondary bg-fm-surface-secondary h-[32rem] overflow-y-auto rounded-xl border p-5 shadow-lg">
|
|
144
|
+
<div className="space-y-5">
|
|
145
|
+
<p className="text-fm-primary font-fm-brand text-fm-sm leading-fm-sm font-semibold tracking-widest uppercase">
|
|
146
|
+
Opacity
|
|
357
147
|
</p>
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
148
|
+
<div className="space-y-2">
|
|
149
|
+
{opacityOptions.map((o) => (
|
|
150
|
+
<Button
|
|
151
|
+
key={o.value}
|
|
152
|
+
size="sm"
|
|
153
|
+
variant={activeOpacity === o.value ? "primary" : "outline"}
|
|
154
|
+
onClick={() =>
|
|
155
|
+
setActiveOpacity(
|
|
156
|
+
activeOpacity === o.value ? null : o.value
|
|
157
|
+
)
|
|
158
|
+
}
|
|
159
|
+
className="w-full justify-start"
|
|
160
|
+
>
|
|
161
|
+
{o.label}
|
|
162
|
+
</Button>
|
|
163
|
+
))}
|
|
164
|
+
</div>
|
|
374
165
|
|
|
375
|
-
|
|
376
|
-
<Overlay opacity="medium" glass="medium">
|
|
377
|
-
<div className="border-fm-divider-secondary bg-fm-surface-secondary/80 max-w-md rounded-lg border p-6">
|
|
378
|
-
<h3 className="text-fm-primary mb-2 text-lg font-semibold">
|
|
379
|
-
Medium Glass Effect
|
|
380
|
-
</h3>
|
|
381
|
-
<p className="text-fm-secondary text-sm">
|
|
382
|
-
Balanced backdrop blur for modern glass aesthetics
|
|
383
|
-
</p>
|
|
384
|
-
</div>
|
|
385
|
-
</Overlay>
|
|
386
|
-
)}
|
|
166
|
+
<div className="border-fm-divider-secondary border-t pt-4" />
|
|
387
167
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
<div className="border-fm-divider-secondary bg-fm-surface-secondary/80 max-w-md rounded-lg border p-6">
|
|
391
|
-
<h3 className="text-fm-primary mb-2 text-lg font-semibold">
|
|
392
|
-
High Glass Effect
|
|
393
|
-
</h3>
|
|
394
|
-
<p className="text-fm-secondary text-sm">
|
|
395
|
-
Strong backdrop blur for premium frosted glass look
|
|
168
|
+
<p className="text-fm-primary font-fm-brand text-fm-sm leading-fm-sm font-semibold tracking-widest uppercase">
|
|
169
|
+
Glass
|
|
396
170
|
</p>
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
// 3. Loading States
|
|
414
|
-
export const LoadingStates: Story = {
|
|
415
|
-
render: () => {
|
|
416
|
-
const [loadingType, setLoadingType] = useState<string | null>(null)
|
|
417
|
-
|
|
418
|
-
const startLoading = (type: string) => {
|
|
419
|
-
setLoadingType(type)
|
|
420
|
-
setTimeout(() => setLoadingType(null), 3000)
|
|
421
|
-
}
|
|
171
|
+
<div className="space-y-2">
|
|
172
|
+
{glassOptions.map((g) => (
|
|
173
|
+
<Button
|
|
174
|
+
key={g.value}
|
|
175
|
+
size="sm"
|
|
176
|
+
variant={activeGlass === g.value ? "primary" : "outline"}
|
|
177
|
+
onClick={() =>
|
|
178
|
+
setActiveGlass(activeGlass === g.value ? null : g.value)
|
|
179
|
+
}
|
|
180
|
+
className="w-full justify-start"
|
|
181
|
+
>
|
|
182
|
+
{g.label}
|
|
183
|
+
</Button>
|
|
184
|
+
))}
|
|
185
|
+
</div>
|
|
422
186
|
|
|
423
|
-
|
|
424
|
-
<div className="relative">
|
|
425
|
-
<BackgroundContent />
|
|
426
|
-
|
|
427
|
-
{/* Control Panel */}
|
|
428
|
-
<div className="fixed top-4 left-1/2 z-50 -translate-x-1/2 transform">
|
|
429
|
-
<div className="bg-fm-surface-primary border-fm-divider-secondary space-y-2 rounded-lg border p-4 backdrop-blur-sm">
|
|
430
|
-
<h3 className="text-fm-primary text-center text-sm font-medium">
|
|
431
|
-
Loading States
|
|
432
|
-
</h3>
|
|
433
|
-
<div className="flex gap-2">
|
|
434
|
-
<Button
|
|
435
|
-
size="sm"
|
|
436
|
-
onClick={() => startLoading("spinner")}
|
|
437
|
-
disabled={!!loadingType}
|
|
438
|
-
>
|
|
439
|
-
Spinner Loading
|
|
440
|
-
</Button>
|
|
441
|
-
<Button
|
|
442
|
-
size="sm"
|
|
443
|
-
onClick={() => startLoading("progress")}
|
|
444
|
-
disabled={!!loadingType}
|
|
445
|
-
>
|
|
446
|
-
Progress Loading
|
|
447
|
-
</Button>
|
|
448
|
-
<Button
|
|
449
|
-
size="sm"
|
|
450
|
-
onClick={() => startLoading("dots")}
|
|
451
|
-
disabled={!!loadingType}
|
|
452
|
-
>
|
|
453
|
-
Dots Loading
|
|
454
|
-
</Button>
|
|
455
|
-
</div>
|
|
456
|
-
</div>
|
|
457
|
-
</div>
|
|
187
|
+
<div className="border-fm-divider-secondary border-t pt-4" />
|
|
458
188
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
<Overlay opacity="high" glass="medium">
|
|
462
|
-
<div className="text-fm-primary text-center">
|
|
463
|
-
<div className="border-fm-primary mx-auto mb-4 h-12 w-12 animate-spin rounded-full border-b-2"></div>
|
|
464
|
-
<h3 className="mb-2 text-lg font-medium">Loading...</h3>
|
|
465
|
-
<p className="text-fm-secondary">
|
|
466
|
-
Please wait while we process your request
|
|
189
|
+
<p className="text-fm-primary font-fm-brand text-fm-sm leading-fm-sm font-semibold tracking-widest uppercase">
|
|
190
|
+
Noise
|
|
467
191
|
</p>
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
</div>
|
|
483
|
-
<p className="text-fm-secondary">
|
|
484
|
-
65% complete - 3 of 5 files uploaded
|
|
485
|
-
</p>
|
|
192
|
+
<div className="space-y-2">
|
|
193
|
+
{noiseOptions.map((n) => (
|
|
194
|
+
<Button
|
|
195
|
+
key={n.value}
|
|
196
|
+
size="sm"
|
|
197
|
+
variant={activeNoise === n.value ? "primary" : "outline"}
|
|
198
|
+
onClick={() =>
|
|
199
|
+
setActiveNoise(activeNoise === n.value ? null : n.value)
|
|
200
|
+
}
|
|
201
|
+
className="w-full justify-start"
|
|
202
|
+
>
|
|
203
|
+
{n.label}
|
|
204
|
+
</Button>
|
|
205
|
+
))}
|
|
486
206
|
</div>
|
|
487
|
-
</div>
|
|
488
|
-
</Overlay>
|
|
489
|
-
)}
|
|
490
207
|
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
</div>
|
|
505
|
-
<h3 className="mb-2 text-lg font-medium">Processing</h3>
|
|
506
|
-
<p className="text-fm-secondary">Analyzing your data...</p>
|
|
208
|
+
{hasActive && (
|
|
209
|
+
<>
|
|
210
|
+
<div className="border-fm-divider-secondary border-t pt-4" />
|
|
211
|
+
<Button
|
|
212
|
+
size="sm"
|
|
213
|
+
variant="secondary"
|
|
214
|
+
onClick={clearAll}
|
|
215
|
+
className="w-full"
|
|
216
|
+
>
|
|
217
|
+
Clear overlay
|
|
218
|
+
</Button>
|
|
219
|
+
</>
|
|
220
|
+
)}
|
|
507
221
|
</div>
|
|
508
|
-
</
|
|
509
|
-
)}
|
|
510
|
-
</div>
|
|
511
|
-
)
|
|
512
|
-
},
|
|
513
|
-
parameters: {
|
|
514
|
-
docs: {
|
|
515
|
-
description: {
|
|
516
|
-
story:
|
|
517
|
-
"Common loading state patterns using overlays with different animations and progress indicators.",
|
|
518
|
-
},
|
|
519
|
-
},
|
|
520
|
-
},
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
// 4. Interactive Examples
|
|
524
|
-
export const InteractiveExamples: Story = {
|
|
525
|
-
render: () => {
|
|
526
|
-
const [showSearch, setShowSearch] = useState(false)
|
|
527
|
-
const [showSettings, setShowSettings] = useState(false)
|
|
528
|
-
const [showConfirm, setShowConfirm] = useState(false)
|
|
222
|
+
</div>
|
|
529
223
|
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
{/* Control Panel */}
|
|
535
|
-
<div className="fixed top-4 left-1/2 z-50 -translate-x-1/2 transform">
|
|
536
|
-
<div className="bg-fm-surface-primary border-fm-divider-secondary space-y-2 rounded-lg border p-4 backdrop-blur-sm">
|
|
537
|
-
<h3 className="text-fm-primary text-center text-sm font-medium">
|
|
538
|
-
Interactive Examples
|
|
539
|
-
</h3>
|
|
540
|
-
<div className="flex gap-2">
|
|
541
|
-
<Button size="sm" onClick={() => setShowSearch(true)}>
|
|
542
|
-
Search Modal
|
|
543
|
-
</Button>
|
|
544
|
-
<Button size="sm" onClick={() => setShowSettings(true)}>
|
|
545
|
-
Settings
|
|
546
|
-
</Button>
|
|
547
|
-
<Button
|
|
548
|
-
size="sm"
|
|
549
|
-
variant="primary"
|
|
550
|
-
onClick={() => setShowConfirm(true)}
|
|
551
|
-
>
|
|
552
|
-
Confirm Action
|
|
553
|
-
</Button>
|
|
224
|
+
{/* ── Preview stage ── */}
|
|
225
|
+
<div className="relative overflow-hidden rounded-2xl">
|
|
226
|
+
<div className="h-[32rem] overflow-hidden rounded-2xl">
|
|
227
|
+
<BackingContent />
|
|
554
228
|
</div>
|
|
555
|
-
</div>
|
|
556
|
-
</div>
|
|
557
229
|
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
className="text-fm-primary hover:bg-fm-surface-secondary rounded-full p-2"
|
|
569
|
-
>
|
|
570
|
-
<CrossIcon className="h-4 w-4" />
|
|
571
|
-
</button>
|
|
572
|
-
</div>
|
|
573
|
-
<div className="space-y-4">
|
|
574
|
-
<div className="relative">
|
|
575
|
-
<SearchIcon className="text-fm-secondary absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2" />
|
|
576
|
-
<input
|
|
577
|
-
type="text"
|
|
578
|
-
placeholder="Search anything..."
|
|
579
|
-
className="border-fm-divider-secondary bg-fm-surface-secondary text-fm-primary placeholder:text-fm-tertiary focus:border-fm-divider-primary w-full rounded-lg border py-3 pr-4 pl-10 focus:outline-none"
|
|
580
|
-
/>
|
|
581
|
-
</div>
|
|
582
|
-
<div className="space-y-2">
|
|
583
|
-
{["Recent searches", "Popular items", "Suggestions"].map(
|
|
584
|
-
(item, i) => (
|
|
585
|
-
<div
|
|
586
|
-
key={i}
|
|
587
|
-
className="hover:bg-fm-surface-secondary flex items-center gap-3 rounded-lg p-3"
|
|
588
|
-
>
|
|
589
|
-
<SearchIcon className="text-fm-secondary h-4 w-4" />
|
|
590
|
-
<span className="text-fm-primary">{item}</span>
|
|
591
|
-
</div>
|
|
592
|
-
)
|
|
593
|
-
)}
|
|
230
|
+
{!hasActive && (
|
|
231
|
+
<div className="pointer-events-none absolute inset-x-6 bottom-6 z-20">
|
|
232
|
+
<div className="border-fm-divider-secondary bg-fm-surface-secondary/90 ml-auto max-w-sm rounded-xl border p-4 backdrop-blur-sm">
|
|
233
|
+
<p className="text-fm-primary font-fm-brand text-fm-md leading-fm-md mb-1 font-semibold">
|
|
234
|
+
Overlay Preview
|
|
235
|
+
</p>
|
|
236
|
+
<p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-xl">
|
|
237
|
+
Choose opacity, glass, and noise options from the left to
|
|
238
|
+
preview the overlay over this bounded content stage.
|
|
239
|
+
</p>
|
|
594
240
|
</div>
|
|
595
241
|
</div>
|
|
596
|
-
|
|
597
|
-
</Overlay>
|
|
598
|
-
)}
|
|
242
|
+
)}
|
|
599
243
|
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
</
|
|
629
|
-
<
|
|
244
|
+
{/* ── Active overlay ── */}
|
|
245
|
+
{hasActive && (
|
|
246
|
+
<Overlay
|
|
247
|
+
opacity={activeOpacity ?? "medium"}
|
|
248
|
+
glass={activeGlass ?? "none"}
|
|
249
|
+
noise={activeNoise ?? "none"}
|
|
250
|
+
className="absolute inset-0 z-10"
|
|
251
|
+
classes={{
|
|
252
|
+
wrapper: "absolute inset-0",
|
|
253
|
+
content: "px-6",
|
|
254
|
+
}}
|
|
255
|
+
>
|
|
256
|
+
<div className="border-fm-divider-secondary bg-fm-surface-secondary max-w-sm rounded-xl border p-6">
|
|
257
|
+
<p className="text-fm-primary font-fm-brand text-fm-lg leading-fm-lg mb-1 font-semibold">
|
|
258
|
+
Active overlay
|
|
259
|
+
</p>
|
|
260
|
+
<div className="text-fm-secondary font-fm-text text-fm-sm leading-fm-xl space-y-0.5">
|
|
261
|
+
<p>
|
|
262
|
+
Opacity:{" "}
|
|
263
|
+
<span className="text-fm-primary">
|
|
264
|
+
{activeOpacity ?? "medium"}
|
|
265
|
+
</span>
|
|
266
|
+
</p>
|
|
267
|
+
<p>
|
|
268
|
+
Glass:{" "}
|
|
269
|
+
<span className="text-fm-primary">
|
|
270
|
+
{activeGlass ?? "none"}
|
|
271
|
+
</span>
|
|
272
|
+
</p>
|
|
273
|
+
<p>
|
|
274
|
+
Noise:{" "}
|
|
275
|
+
<span className="text-fm-primary">
|
|
276
|
+
{activeNoise ?? "none"}
|
|
277
|
+
</span>
|
|
278
|
+
</p>
|
|
630
279
|
</div>
|
|
631
|
-
))}
|
|
632
|
-
</div>
|
|
633
|
-
</div>
|
|
634
|
-
</Overlay>
|
|
635
|
-
)}
|
|
636
|
-
|
|
637
|
-
{/* Confirmation Modal */}
|
|
638
|
-
{showConfirm && (
|
|
639
|
-
<Overlay opacity="high" glass="high">
|
|
640
|
-
<div className="border-fm-divider-negative bg-fm-surface-negative-sec w-full max-w-sm rounded-lg border p-6 backdrop-blur-sm">
|
|
641
|
-
<div className="text-center">
|
|
642
|
-
<AlertIcon className="text-fm-negative mx-auto mb-4 h-12 w-12" />
|
|
643
|
-
<h3 className="text-fm-primary mb-2 text-lg font-semibold">
|
|
644
|
-
Confirm Action
|
|
645
|
-
</h3>
|
|
646
|
-
<p className="text-fm-secondary mb-6 text-sm">
|
|
647
|
-
This action cannot be undone. Are you sure you want to
|
|
648
|
-
continue?
|
|
649
|
-
</p>
|
|
650
|
-
<div className="flex gap-3">
|
|
651
280
|
<Button
|
|
652
|
-
variant="outline"
|
|
653
|
-
size="sm"
|
|
654
|
-
onClick={() => setShowConfirm(false)}
|
|
655
|
-
className="flex-1"
|
|
656
|
-
>
|
|
657
|
-
Cancel
|
|
658
|
-
</Button>
|
|
659
|
-
<Button
|
|
660
|
-
variant="secondary"
|
|
661
281
|
size="sm"
|
|
662
|
-
|
|
663
|
-
|
|
282
|
+
variant="outline"
|
|
283
|
+
onClick={clearAll}
|
|
284
|
+
className="mt-4 w-full"
|
|
664
285
|
>
|
|
665
|
-
|
|
286
|
+
Dismiss
|
|
666
287
|
</Button>
|
|
667
288
|
</div>
|
|
668
|
-
</
|
|
669
|
-
</div>
|
|
670
|
-
</Overlay>
|
|
671
|
-
)}
|
|
672
|
-
</div>
|
|
673
|
-
)
|
|
674
|
-
},
|
|
675
|
-
parameters: {
|
|
676
|
-
docs: {
|
|
677
|
-
description: {
|
|
678
|
-
story:
|
|
679
|
-
"Interactive overlay examples including search modals, settings panels, and confirmation dialogs demonstrating real-world usage patterns.",
|
|
680
|
-
},
|
|
681
|
-
},
|
|
682
|
-
},
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
// 5. Noise Texture Variants
|
|
686
|
-
export const NoiseTextureVariants: Story = {
|
|
687
|
-
render: () => {
|
|
688
|
-
const [activeNoise, setActiveNoise] = useState<string | null>(null)
|
|
689
|
-
|
|
690
|
-
return (
|
|
691
|
-
<div className="relative">
|
|
692
|
-
<BackgroundContent />
|
|
693
|
-
|
|
694
|
-
{/* Control Panel */}
|
|
695
|
-
<div className="fixed bottom-4 left-1/2 z-50 -translate-x-1/2 transform">
|
|
696
|
-
<div className="bg-fm-surface-primary border-fm-divider-secondary space-y-2 rounded-lg border p-4 backdrop-blur-sm">
|
|
697
|
-
<h3 className="text-fm-primary text-center text-sm font-medium">
|
|
698
|
-
Noise Texture Variants
|
|
699
|
-
</h3>
|
|
700
|
-
<div className="flex gap-2">
|
|
701
|
-
<Button
|
|
702
|
-
size="sm"
|
|
703
|
-
variant={activeNoise === "none" ? "primary" : "outline"}
|
|
704
|
-
onClick={() =>
|
|
705
|
-
setActiveNoise(activeNoise === "none" ? null : "none")
|
|
706
|
-
}
|
|
707
|
-
>
|
|
708
|
-
No Noise
|
|
709
|
-
</Button>
|
|
710
|
-
<Button
|
|
711
|
-
size="sm"
|
|
712
|
-
variant={activeNoise === "low" ? "primary" : "outline"}
|
|
713
|
-
onClick={() =>
|
|
714
|
-
setActiveNoise(activeNoise === "low" ? null : "low")
|
|
715
|
-
}
|
|
716
|
-
>
|
|
717
|
-
Low Noise
|
|
718
|
-
</Button>
|
|
719
|
-
<Button
|
|
720
|
-
size="sm"
|
|
721
|
-
variant={activeNoise === "medium" ? "primary" : "outline"}
|
|
722
|
-
onClick={() =>
|
|
723
|
-
setActiveNoise(activeNoise === "medium" ? null : "medium")
|
|
724
|
-
}
|
|
725
|
-
>
|
|
726
|
-
Medium Noise
|
|
727
|
-
</Button>
|
|
728
|
-
<Button
|
|
729
|
-
size="sm"
|
|
730
|
-
variant={activeNoise === "high" ? "primary" : "outline"}
|
|
731
|
-
onClick={() =>
|
|
732
|
-
setActiveNoise(activeNoise === "high" ? null : "high")
|
|
733
|
-
}
|
|
734
|
-
>
|
|
735
|
-
High Noise
|
|
736
|
-
</Button>
|
|
737
|
-
</div>
|
|
738
|
-
{activeNoise && (
|
|
739
|
-
<Button
|
|
740
|
-
size="sm"
|
|
741
|
-
variant="secondary"
|
|
742
|
-
onClick={() => setActiveNoise(null)}
|
|
743
|
-
className="w-full"
|
|
744
|
-
>
|
|
745
|
-
Clear Overlay
|
|
746
|
-
</Button>
|
|
289
|
+
</Overlay>
|
|
747
290
|
)}
|
|
748
291
|
</div>
|
|
749
292
|
</div>
|
|
750
|
-
|
|
751
|
-
{/* Noise Overlays */}
|
|
752
|
-
{activeNoise === "none" && (
|
|
753
|
-
<Overlay opacity="medium" glass="low" noise="none">
|
|
754
|
-
<div className="border-fm-divider-secondary bg-fm-surface-secondary/80 max-w-md rounded-lg border p-6 backdrop-blur-sm">
|
|
755
|
-
<h3 className="text-fm-primary mb-2 text-lg font-semibold">
|
|
756
|
-
No Noise Texture
|
|
757
|
-
</h3>
|
|
758
|
-
<p className="text-fm-secondary text-sm">
|
|
759
|
-
Clean overlay without any texture patterns
|
|
760
|
-
</p>
|
|
761
|
-
</div>
|
|
762
|
-
</Overlay>
|
|
763
|
-
)}
|
|
764
|
-
|
|
765
|
-
{activeNoise === "low" && (
|
|
766
|
-
<Overlay opacity="medium" glass="low" noise="low">
|
|
767
|
-
<div className="border-fm-divider-secondary bg-fm-surface-secondary/80 max-w-md rounded-lg border p-6 backdrop-blur-sm">
|
|
768
|
-
<h3 className="text-fm-primary mb-2 text-lg font-semibold">
|
|
769
|
-
Low Noise Texture
|
|
770
|
-
</h3>
|
|
771
|
-
<p className="text-fm-secondary text-sm">
|
|
772
|
-
Subtle noise pattern for added visual interest
|
|
773
|
-
</p>
|
|
774
|
-
</div>
|
|
775
|
-
</Overlay>
|
|
776
|
-
)}
|
|
777
|
-
|
|
778
|
-
{activeNoise === "medium" && (
|
|
779
|
-
<Overlay opacity="medium" glass="low" noise="medium">
|
|
780
|
-
<div className="border-fm-divider-secondary bg-fm-surface-secondary/80 max-w-md rounded-lg border p-6 backdrop-blur-sm">
|
|
781
|
-
<h3 className="text-fm-primary mb-2 text-lg font-semibold">
|
|
782
|
-
Medium Noise Texture
|
|
783
|
-
</h3>
|
|
784
|
-
<p className="text-fm-secondary text-sm">
|
|
785
|
-
Balanced noise pattern for enhanced texture
|
|
786
|
-
</p>
|
|
787
|
-
</div>
|
|
788
|
-
</Overlay>
|
|
789
|
-
)}
|
|
790
|
-
|
|
791
|
-
{activeNoise === "high" && (
|
|
792
|
-
<Overlay opacity="medium" glass="low" noise="high">
|
|
793
|
-
<div className="border-fm-divider-secondary bg-fm-surface-secondary/80 max-w-md rounded-lg border p-6 backdrop-blur-sm">
|
|
794
|
-
<h3 className="text-fm-primary mb-2 text-lg font-semibold">
|
|
795
|
-
High Noise Texture
|
|
796
|
-
</h3>
|
|
797
|
-
<p className="text-fm-secondary text-sm">
|
|
798
|
-
Prominent noise pattern for dramatic effect
|
|
799
|
-
</p>
|
|
800
|
-
</div>
|
|
801
|
-
</Overlay>
|
|
802
|
-
)}
|
|
803
293
|
</div>
|
|
804
294
|
)
|
|
805
295
|
},
|
|
@@ -807,144 +297,282 @@ export const NoiseTextureVariants: Story = {
|
|
|
807
297
|
docs: {
|
|
808
298
|
description: {
|
|
809
299
|
story:
|
|
810
|
-
"
|
|
300
|
+
"Interactive panel to explore every configuration axis independently: opacity levels (40 % / 60 % / 80 % / solid), glass blur (none → high), and noise texture (none → strong). Mix and match any combination to preview the live effect over real backing content.",
|
|
811
301
|
},
|
|
812
302
|
},
|
|
813
303
|
},
|
|
814
304
|
}
|
|
815
305
|
|
|
816
|
-
//
|
|
817
|
-
export const CombinedEffectsShowcase: Story = {
|
|
818
|
-
render: () => {
|
|
819
|
-
const [currentCombo, setCurrentCombo] = useState<string | null>(null)
|
|
306
|
+
// ─── 2. Showcase ──────────────────────────────────────────────────────────────
|
|
820
307
|
|
|
821
|
-
|
|
308
|
+
export const Showcase: Story = {
|
|
309
|
+
render: () => {
|
|
310
|
+
/**
|
|
311
|
+
* Each card shows the Overlay rendered inside a bounded relative container
|
|
312
|
+
* (position: relative on the wrapper, not fixed on the overlay) so the
|
|
313
|
+
* gallery fits on-canvas. We override `className` to switch from
|
|
314
|
+
* `fixed inset-0` to `absolute inset-0` for in-card previews.
|
|
315
|
+
*/
|
|
316
|
+
const combos = [
|
|
822
317
|
{
|
|
823
|
-
id: "minimal",
|
|
824
318
|
name: "Minimal",
|
|
319
|
+
meta: "opacity: low · glass: none · noise: none",
|
|
825
320
|
opacity: "low",
|
|
826
321
|
glass: "none",
|
|
827
322
|
noise: "none",
|
|
323
|
+
backing: "bg-fm-surface-primary",
|
|
828
324
|
},
|
|
829
325
|
{
|
|
830
|
-
id: "subtle",
|
|
831
326
|
name: "Subtle",
|
|
327
|
+
meta: "opacity: medium · glass: low · noise: low",
|
|
832
328
|
opacity: "medium",
|
|
833
329
|
glass: "low",
|
|
834
330
|
noise: "low",
|
|
331
|
+
backing: "bg-fm-surface-secondary",
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
name: "Frosted",
|
|
335
|
+
meta: "opacity: medium · glass: high · noise: none",
|
|
336
|
+
opacity: "medium",
|
|
337
|
+
glass: "high",
|
|
338
|
+
noise: "none",
|
|
339
|
+
backing: "bg-fm-surface-contrast",
|
|
835
340
|
},
|
|
836
341
|
{
|
|
837
|
-
id: "balanced",
|
|
838
342
|
name: "Balanced",
|
|
343
|
+
meta: "opacity: medium · glass: medium · noise: medium",
|
|
839
344
|
opacity: "medium",
|
|
840
345
|
glass: "medium",
|
|
841
346
|
noise: "medium",
|
|
347
|
+
backing: "bg-fm-surface-secondary",
|
|
842
348
|
},
|
|
843
349
|
{
|
|
844
|
-
id: "dramatic",
|
|
845
350
|
name: "Dramatic",
|
|
351
|
+
meta: "opacity: high · glass: high · noise: low",
|
|
846
352
|
opacity: "high",
|
|
847
353
|
glass: "high",
|
|
848
354
|
noise: "low",
|
|
355
|
+
backing: "bg-fm-surface-primary",
|
|
849
356
|
},
|
|
850
357
|
{
|
|
851
|
-
|
|
852
|
-
|
|
358
|
+
name: "Textured",
|
|
359
|
+
meta: "opacity: high · glass: medium · noise: high",
|
|
853
360
|
opacity: "high",
|
|
854
|
-
glass: "
|
|
361
|
+
glass: "medium",
|
|
855
362
|
noise: "high",
|
|
363
|
+
backing: "bg-fm-surface-contrast",
|
|
856
364
|
},
|
|
857
365
|
] as const
|
|
858
366
|
|
|
859
367
|
return (
|
|
860
|
-
<div className="
|
|
861
|
-
<
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
</h3>
|
|
869
|
-
<div className="space-y-2">
|
|
870
|
-
{combinations.map((combo) => (
|
|
871
|
-
<Button
|
|
872
|
-
key={combo.id}
|
|
873
|
-
size="sm"
|
|
874
|
-
variant={currentCombo === combo.id ? "primary" : "outline"}
|
|
875
|
-
onClick={() =>
|
|
876
|
-
setCurrentCombo(currentCombo === combo.id ? null : combo.id)
|
|
877
|
-
}
|
|
878
|
-
className="w-full justify-start"
|
|
368
|
+
<div className="w-full p-8">
|
|
369
|
+
<div className="mx-auto max-w-4xl space-y-10">
|
|
370
|
+
<div className="grid grid-cols-2 gap-6 sm:grid-cols-3">
|
|
371
|
+
{combos.map((combo) => (
|
|
372
|
+
<div key={combo.name} className="group space-y-3">
|
|
373
|
+
{/* Bounded preview card */}
|
|
374
|
+
<div
|
|
375
|
+
className={`relative h-40 w-full overflow-hidden rounded-xl ${combo.backing}`}
|
|
879
376
|
>
|
|
880
|
-
{
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
>
|
|
891
|
-
Clear Overlay
|
|
892
|
-
</Button>
|
|
893
|
-
)}
|
|
894
|
-
</div>
|
|
895
|
-
</div>
|
|
377
|
+
{/* Mock content in the "background" */}
|
|
378
|
+
<div className="absolute inset-0 flex flex-col gap-2 p-4">
|
|
379
|
+
<div className="border-fm-divider-secondary bg-fm-surface-secondary h-6 w-3/4 rounded" />
|
|
380
|
+
<div className="border-fm-divider-secondary bg-fm-surface-secondary h-4 w-full rounded" />
|
|
381
|
+
<div className="border-fm-divider-secondary bg-fm-surface-secondary h-4 w-5/6 rounded" />
|
|
382
|
+
<div className="border-fm-divider-secondary bg-fm-surface-secondary mt-auto flex gap-2">
|
|
383
|
+
<div className="h-7 w-16 rounded" />
|
|
384
|
+
<div className="h-7 w-20 rounded" />
|
|
385
|
+
</div>
|
|
386
|
+
</div>
|
|
896
387
|
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
<div className="text-fm-secondary space-y-1 text-xs">
|
|
905
|
-
<div>
|
|
906
|
-
Opacity:{" "}
|
|
907
|
-
{combinations.find((c) => c.id === currentCombo)?.opacity}
|
|
908
|
-
</div>
|
|
909
|
-
<div>
|
|
910
|
-
Glass:{" "}
|
|
911
|
-
{combinations.find((c) => c.id === currentCombo)?.glass}
|
|
388
|
+
{/* Overlay — absolute instead of fixed for bounded card display */}
|
|
389
|
+
<Overlay
|
|
390
|
+
opacity={combo.opacity}
|
|
391
|
+
glass={combo.glass}
|
|
392
|
+
noise={combo.noise}
|
|
393
|
+
className="absolute inset-0 z-10"
|
|
394
|
+
/>
|
|
912
395
|
</div>
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
396
|
+
|
|
397
|
+
{/* Item metadata */}
|
|
398
|
+
<div className="space-y-0.5 text-center">
|
|
399
|
+
<p className="text-fm-primary font-fm-brand text-fm-md leading-fm-md font-semibold">
|
|
400
|
+
{combo.name}
|
|
401
|
+
</p>
|
|
402
|
+
<p className="text-fm-tertiary font-fm-text text-fm-sm leading-fm-sm">
|
|
403
|
+
{combo.meta}
|
|
404
|
+
</p>
|
|
916
405
|
</div>
|
|
917
406
|
</div>
|
|
918
|
-
|
|
407
|
+
))}
|
|
919
408
|
</div>
|
|
920
|
-
)}
|
|
921
409
|
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
<
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
410
|
+
{/* Info box */}
|
|
411
|
+
<div className="border-fm-divider-secondary bg-fm-surface-secondary rounded-lg border p-4">
|
|
412
|
+
<p className="text-fm-secondary font-fm-text text-fm-md leading-fm-xl">
|
|
413
|
+
Each card above uses a design-token background (
|
|
414
|
+
<code className="text-fm-primary font-(--font-fm-mono)">
|
|
415
|
+
bg-fm-surface-primary
|
|
416
|
+
</code>
|
|
417
|
+
,{" "}
|
|
418
|
+
<code className="text-fm-primary font-(--font-fm-mono)">
|
|
419
|
+
bg-fm-surface-secondary
|
|
420
|
+
</code>
|
|
421
|
+
,{" "}
|
|
422
|
+
<code className="text-fm-primary font-(--font-fm-mono)">
|
|
423
|
+
bg-fm-surface-contrast
|
|
424
|
+
</code>
|
|
425
|
+
) — no hardcoded gradient colors. The{" "}
|
|
426
|
+
<code className="text-fm-primary font-(--font-fm-mono)">
|
|
427
|
+
className
|
|
428
|
+
</code>{" "}
|
|
429
|
+
prop switches the overlay from{" "}
|
|
430
|
+
<code className="text-fm-primary">fixed</code> to{" "}
|
|
431
|
+
<code className="text-fm-primary">absolute</code> for bounded
|
|
432
|
+
in-card previews.
|
|
433
|
+
</p>
|
|
434
|
+
</div>
|
|
435
|
+
</div>
|
|
436
|
+
</div>
|
|
437
|
+
)
|
|
438
|
+
},
|
|
439
|
+
parameters: {
|
|
440
|
+
layout: "fullscreen",
|
|
441
|
+
docs: {
|
|
442
|
+
description: {
|
|
443
|
+
story:
|
|
444
|
+
"Curated gallery of six overlay combinations — from minimal (low opacity, no glass) to textured (high opacity, medium glass, strong noise). Each card uses design-token backgrounds (bg-fm-surface-primary, bg-fm-surface-secondary, bg-fm-surface-contrast) with no hardcoded Tailwind gradient colors.",
|
|
445
|
+
},
|
|
446
|
+
},
|
|
447
|
+
},
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// ─── 3. Interactive ───────────────────────────────────────────────────────────
|
|
451
|
+
|
|
452
|
+
export const Interactive: Story = {
|
|
453
|
+
render: () => {
|
|
454
|
+
const [showSearch, setShowSearch] = useState(false)
|
|
455
|
+
const [showConfirm, setShowConfirm] = useState(false)
|
|
456
|
+
const [searchQuery, setSearchQuery] = useState("")
|
|
457
|
+
|
|
458
|
+
return (
|
|
459
|
+
<div className="relative">
|
|
460
|
+
<BackingContent />
|
|
461
|
+
|
|
462
|
+
{/* Trigger buttons */}
|
|
463
|
+
<div className="fixed top-4 left-1/2 z-50 -translate-x-1/2">
|
|
464
|
+
<div className="border-fm-divider-secondary bg-fm-surface-secondary flex gap-3 rounded-xl border p-4">
|
|
465
|
+
<Button size="sm" onClick={() => setShowSearch(true)}>
|
|
466
|
+
Search
|
|
467
|
+
</Button>
|
|
468
|
+
<Button
|
|
469
|
+
size="sm"
|
|
470
|
+
variant="secondary"
|
|
471
|
+
onClick={() => setShowConfirm(true)}
|
|
472
|
+
>
|
|
473
|
+
Confirm action
|
|
474
|
+
</Button>
|
|
475
|
+
</div>
|
|
476
|
+
</div>
|
|
477
|
+
|
|
478
|
+
{/* Search modal */}
|
|
479
|
+
{showSearch && (
|
|
480
|
+
<Overlay opacity="medium" glass="medium" noise="none">
|
|
481
|
+
<div className="border-fm-divider-secondary bg-fm-surface-secondary w-full max-w-lg rounded-xl border p-6">
|
|
482
|
+
<div className="mb-4 flex items-center justify-between">
|
|
483
|
+
<p className="text-fm-primary font-fm-brand text-fm-lg leading-fm-lg font-semibold">
|
|
484
|
+
Search
|
|
485
|
+
</p>
|
|
486
|
+
<button
|
|
487
|
+
onClick={() => {
|
|
488
|
+
setShowSearch(false)
|
|
489
|
+
setSearchQuery("")
|
|
490
|
+
}}
|
|
491
|
+
className="text-fm-secondary hover:text-fm-primary rounded-full p-1 transition-colors"
|
|
492
|
+
aria-label="Close search"
|
|
493
|
+
>
|
|
494
|
+
<CrossIcon className="h-4 w-4" />
|
|
495
|
+
</button>
|
|
496
|
+
</div>
|
|
497
|
+
|
|
498
|
+
<div className="relative mb-4">
|
|
499
|
+
<SearchIcon className="text-fm-tertiary absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2" />
|
|
500
|
+
<input
|
|
501
|
+
type="text"
|
|
502
|
+
placeholder="Search artists, albums, tracks…"
|
|
503
|
+
value={searchQuery}
|
|
504
|
+
onChange={(e) => setSearchQuery(e.target.value)}
|
|
505
|
+
className="border-fm-divider-secondary bg-fm-surface-secondary text-fm-primary placeholder:text-fm-tertiary focus:border-fm-divider-primary font-fm-text text-fm-sm leading-fm-sm w-full rounded-lg border py-3 pr-4 pl-10 focus:outline-none"
|
|
506
|
+
autoFocus
|
|
507
|
+
/>
|
|
508
|
+
</div>
|
|
509
|
+
|
|
510
|
+
{searchQuery.length === 0 && (
|
|
511
|
+
<div className="space-y-1">
|
|
512
|
+
<p className="text-fm-secondary font-fm-brand text-fm-sm leading-fm-sm mb-2 font-semibold tracking-widest uppercase">
|
|
513
|
+
Recent
|
|
944
514
|
</p>
|
|
515
|
+
{["Bicep", "Four Tet", "Floating Points"].map((item) => (
|
|
516
|
+
<button
|
|
517
|
+
key={item}
|
|
518
|
+
onClick={() => setSearchQuery(item)}
|
|
519
|
+
className="hover:bg-fm-surface-tertiary flex w-full items-center gap-3 rounded-lg px-3 py-2 text-left transition-colors"
|
|
520
|
+
>
|
|
521
|
+
<SearchIcon className="text-fm-tertiary h-4 w-4 shrink-0" />
|
|
522
|
+
<span className="text-fm-primary font-fm-text text-fm-sm leading-fm-sm">
|
|
523
|
+
{item}
|
|
524
|
+
</span>
|
|
525
|
+
</button>
|
|
526
|
+
))}
|
|
945
527
|
</div>
|
|
946
|
-
|
|
947
|
-
|
|
528
|
+
)}
|
|
529
|
+
|
|
530
|
+
{searchQuery.length > 0 && (
|
|
531
|
+
<p className="text-fm-tertiary font-fm-text text-fm-sm leading-fm-sm">
|
|
532
|
+
Showing results for{" "}
|
|
533
|
+
<span className="text-fm-primary">"{searchQuery}"</span>…
|
|
534
|
+
</p>
|
|
535
|
+
)}
|
|
536
|
+
</div>
|
|
537
|
+
</Overlay>
|
|
538
|
+
)}
|
|
539
|
+
|
|
540
|
+
{/* Confirmation dialog */}
|
|
541
|
+
{showConfirm && (
|
|
542
|
+
<Overlay opacity="high" glass="high" noise="none">
|
|
543
|
+
<div className="border-fm-divider-secondary bg-fm-surface-secondary max-w-sm rounded-xl border p-6 text-center">
|
|
544
|
+
<div className="border-fm-divider-secondary bg-fm-surface-tertiary mx-auto mb-4 flex h-12 w-12 items-center justify-center rounded-full">
|
|
545
|
+
<span className="text-fm-primary font-fm-brand text-fm-lg leading-fm-lg font-bold">
|
|
546
|
+
!
|
|
547
|
+
</span>
|
|
548
|
+
</div>
|
|
549
|
+
<p className="text-fm-primary font-fm-brand text-fm-lg leading-fm-lg mb-2 font-semibold">
|
|
550
|
+
Remove from library?
|
|
551
|
+
</p>
|
|
552
|
+
<p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-xl mb-6">
|
|
553
|
+
This will remove the album from your library. You can always add
|
|
554
|
+
it back later.
|
|
555
|
+
</p>
|
|
556
|
+
<div className="flex gap-3">
|
|
557
|
+
<Button
|
|
558
|
+
variant="outline"
|
|
559
|
+
size="sm"
|
|
560
|
+
onClick={() => setShowConfirm(false)}
|
|
561
|
+
className="flex-1"
|
|
562
|
+
>
|
|
563
|
+
Cancel
|
|
564
|
+
</Button>
|
|
565
|
+
<Button
|
|
566
|
+
variant="secondary"
|
|
567
|
+
size="sm"
|
|
568
|
+
onClick={() => setShowConfirm(false)}
|
|
569
|
+
className="flex-1"
|
|
570
|
+
>
|
|
571
|
+
Remove
|
|
572
|
+
</Button>
|
|
573
|
+
</div>
|
|
574
|
+
</div>
|
|
575
|
+
</Overlay>
|
|
948
576
|
)}
|
|
949
577
|
</div>
|
|
950
578
|
)
|
|
@@ -953,7 +581,7 @@ export const CombinedEffectsShowcase: Story = {
|
|
|
953
581
|
docs: {
|
|
954
582
|
description: {
|
|
955
583
|
story:
|
|
956
|
-
"
|
|
584
|
+
"Live interaction examples: a search modal (medium opacity, medium glass) and a confirmation dialog (high opacity, high glass). Demonstrates real-world overlay usage with controlled open/close state and accessible focus management.",
|
|
957
585
|
},
|
|
958
586
|
},
|
|
959
587
|
},
|