aural-ui 3.0.7 → 4.1.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/dist/components/aspect-ratio/AspectRatio.stories.tsx +290 -1199
- package/dist/components/avatar/Avatar.stories.tsx +235 -237
- 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/button/index.tsx +7 -7
- 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 -620
- 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 +533 -856
- package/dist/components/dialog/Dialog.stories.tsx +505 -949
- package/dist/components/divider/Divider.stories.tsx +265 -502
- package/dist/components/dot-loader/DotLoader.stories.tsx +256 -257
- package/dist/components/drawer/Drawer.stories.tsx +659 -993
- package/dist/components/drawer/index.tsx +3 -3
- package/dist/components/dropdown/Dropdown.stories.tsx +643 -1018
- 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 -1221
- 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 +485 -822
- package/dist/components/marquee/Marquee.stories.tsx +356 -694
- package/dist/components/otp-inputs/OtpInputs.stories.tsx +352 -410
- package/dist/components/overlay/Overlay.stories.tsx +452 -818
- package/dist/components/overlay/index.tsx +4 -4
- package/dist/components/pagination/Pagination.stories.tsx +721 -210
- package/dist/components/popover/Popover.stories.tsx +484 -873
- package/dist/components/radio/Radio.stories.tsx +432 -124
- package/dist/components/resizable/Resizable.stories.tsx +496 -752
- package/dist/components/scroll-area/ScrollArea.stories.tsx +384 -1006
- package/dist/components/search/Search.stories.tsx +314 -575
- package/dist/components/select/Select.stories.tsx +684 -787
- package/dist/components/sheet/Sheet.stories.tsx +671 -936
- package/dist/components/skelton/Skelton.stories.tsx +230 -764
- package/dist/components/slider/Slider.stories.tsx +384 -737
- 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 -914
- package/dist/components/tabs/Tabs.stories.tsx +459 -1400
- 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 -148
- package/dist/components/toast/Toast.stories.tsx +452 -1333
- package/dist/components/toggle/Toggle.stories.tsx +488 -909
- package/dist/components/tooltip/Tooltip.stories.tsx +344 -1372
- 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 +226 -1013
- package/dist/icons/alert-icon/AlertIcon.stories.tsx +109 -929
- package/dist/icons/all-icons.tsx +124 -87
- package/dist/icons/angle-down-icon/AngleDownIcon.stories.tsx +140 -971
- package/dist/icons/apple-logo-icon/AppleLogoIcon.stories.tsx +148 -888
- package/dist/icons/arrow-box-left-icon/ArrowBoxLeftIcon.stories.tsx +135 -1019
- package/dist/icons/arrow-corner-up-left-icon/ArrowCornerUpLeftIcon.stories.tsx +137 -953
- package/dist/icons/arrow-corner-up-right-icon/ArrowCornerUpRightIcon.stories.tsx +138 -997
- package/dist/icons/arrow-left-icon/ArrowLeftIcon.stories.tsx +136 -942
- package/dist/icons/arrow-right-icon/ArrowRightIcon.stories.tsx +148 -1092
- package/dist/icons/arrow-right-up-icon/ArrowRightUpIcon.stories.tsx +146 -1211
- package/dist/icons/art-board-icon/ArtBoardIcon.stories.tsx +126 -615
- package/dist/icons/audio-bar-icon/AudioBarIcon.stories.tsx +144 -1164
- package/dist/icons/backward-ten-seconds-icon/BackwardTenSecondsIcon.stories.tsx +167 -985
- package/dist/icons/bubble-check-icon/BubbleCheckIcon.stories.tsx +122 -1179
- package/dist/icons/bubble-crossed-icon/BubbleCrossedIcon.stories.tsx +124 -1168
- package/dist/icons/bubble-sparkle-icon/BubbleSparkleIcon.stories.tsx +119 -850
- package/dist/icons/camera-icon/CameraIcon.stories.tsx +112 -1213
- package/dist/icons/capital-a-letter-icon/CapitalALetterIcon.stories.tsx +117 -934
- package/dist/icons/chevron-double-left-icon/ChevronDoubleLeftIcon.stories.tsx +160 -961
- package/dist/icons/chevron-double-right-icon/ChevronDoubleRightIcon.stories.tsx +163 -961
- package/dist/icons/chevron-down-icon/ChevronDownIcon.stories.tsx +144 -942
- package/dist/icons/chevron-left-icon/ChevronLeftIcon.stories.tsx +129 -966
- package/dist/icons/chevron-right-icon/ChevronRightIcon.stories.tsx +147 -964
- package/dist/icons/chevron-up-icon/ChevronUpIcon.stories.tsx +145 -975
- package/dist/icons/circle-tick-icon/CircleTickIcon.stories.tsx +150 -1142
- package/dist/icons/circular-play-icon/CircularPlayIcon.stories.tsx +114 -461
- package/dist/icons/coin-icon/CoinIcon.stories.tsx +124 -1322
- package/dist/icons/coin-toons-icon/CoinToonsIcon.stories.tsx +117 -1318
- package/dist/icons/column-wide-add-icon/ColumnWideAddIcon.stories.tsx +114 -903
- package/dist/icons/command-icon/CommandIcon.stories.tsx +127 -1042
- package/dist/icons/copy-icon/CopyIcon.stories.tsx +123 -962
- package/dist/icons/cross-circle-icon/CrossCircleIcon.stories.tsx +147 -999
- package/dist/icons/cross-icon/CrossIcon.stories.tsx +139 -960
- package/dist/icons/download-icon/DownloadIcon.stories.tsx +126 -820
- package/dist/icons/edit-big-icon/EditBigIcon.stories.tsx +124 -1031
- package/dist/icons/email-icon/EmailIcon.stories.tsx +115 -936
- package/dist/icons/expand-icon/ExpandIcon.stories.tsx +112 -1111
- package/dist/icons/eye-close-icon/EyeCloseIcon.stories.tsx +144 -1025
- package/dist/icons/eye-open-icon/EyeOpenIcon.stories.tsx +143 -1036
- package/dist/icons/feature-shine-icon/FeatureShineIcon.stories.tsx +127 -1011
- package/dist/icons/file-chart-icon/FileChartIcon.stories.tsx +126 -1056
- package/dist/icons/file-text-icon/FileTextIcon.stories.tsx +125 -614
- package/dist/icons/filter-bar-row-icon/FilterBarRowIcon.stories.tsx +119 -1050
- package/dist/icons/forward-ten-seconds-icon/ForwardTenSecondsIcon.stories.tsx +169 -989
- package/dist/icons/git-branch-icon/GitBranchIcon.stories.tsx +115 -1145
- package/dist/icons/git-fork-icon/GitForkIcon.stories.tsx +115 -1122
- package/dist/icons/globe-icon/GlobeIcon.stories.tsx +130 -313
- package/dist/icons/google-logo-icon/GoogleLogoIcon.stories.tsx +145 -940
- package/dist/icons/grip-vertical-icon/GripVerticalIcon.stories.tsx +119 -1174
- package/dist/icons/head-icon/HeadIcon.stories.tsx +111 -916
- package/dist/icons/heart-icon/HeartIcon.stories.tsx +120 -1019
- package/dist/icons/image-avatar-sparkle-icon/ImageAvatarSparkleIcon.stories.tsx +119 -683
- package/dist/icons/image-icon/ImageIcon.stories.tsx +105 -1121
- package/dist/icons/import-folder-icon/ImportFolderIcon.stories.tsx +111 -1192
- package/dist/icons/import-left-arrow-folder-icon/ImportLeftArrowFolderIcon.stories.tsx +136 -1256
- package/dist/icons/indian-flag-icon/IndianFlagIcon.stories.tsx +159 -962
- package/dist/icons/instagram-icon/InstagramIcon.stories.tsx +161 -1385
- package/dist/icons/layout-column-icon/LayoutColumnIcon.stories.tsx +124 -972
- package/dist/icons/layout-left-icon/LayoutLeftIcon.stories.tsx +119 -948
- package/dist/icons/layout-right-icon/LayoutRightIcon.stories.tsx +119 -942
- package/dist/icons/light-bulb-simple-icon/LightBulbSimpleIcon.stories.tsx +108 -1215
- package/dist/icons/linked-in-icon/LinkedInIcon.stories.tsx +154 -1517
- package/dist/icons/magic-book-icon/MagicBookIcon.stories.tsx +110 -1188
- package/dist/icons/magic-edit-icon/MagicEditIcon.stories.tsx +119 -678
- package/dist/icons/maintenance-icon/MaintenanceIcon.stories.tsx +123 -1184
- package/dist/icons/message-icon/MessageIcon.stories.tsx +114 -538
- package/dist/icons/minimize-icon/MinimizeIcon.stories.tsx +116 -1158
- package/dist/icons/moon-icon/MoonIcon.stories.tsx +120 -536
- package/dist/icons/move-horizontal-icon/MoveHorizontalIcon.stories.tsx +109 -1184
- package/dist/icons/move-vertical-icon/MoveVerticalIcon.stories.tsx +115 -1134
- package/dist/icons/musical-note-icon/MusicalNoteIcon.stories.tsx +119 -971
- package/dist/icons/notepad-icon/NotepadIcon.stories.tsx +111 -1100
- package/dist/icons/notes-icon/NotesIcon.stories.tsx +119 -1101
- package/dist/icons/page-search-icon/PageSearchIcon.stories.tsx +109 -1111
- package/dist/icons/page-text-icon/PageTextIcon.stories.tsx +122 -684
- package/dist/icons/paint-roll-icon/PaintRollIcon.stories.tsx +113 -954
- package/dist/icons/paper-plane-icon/PaperPlaneIcon.stories.tsx +112 -877
- package/dist/icons/pause-icon/PauseIcon.stories.tsx +113 -1000
- package/dist/icons/pencil-icon/PencilIcon.stories.tsx +115 -1070
- package/dist/icons/phone-icon/PhoneIcon.stories.tsx +115 -978
- package/dist/icons/plus-icon/PlusIcon.stories.tsx +106 -1093
- package/dist/icons/pocket-studio-icon/PocketStudioIcon.stories.tsx +107 -829
- package/dist/icons/scroll-down-icon/ScrollDownIcon.stories.tsx +102 -469
- package/dist/icons/search-icon/SearchIcon.stories.tsx +111 -1124
- package/dist/icons/setting-icon/SettingIcon.stories.tsx +107 -970
- package/dist/icons/share-icon/ShareIcon.stories.tsx +120 -1025
- package/dist/icons/shield-icon/ShieldIcon.stories.tsx +117 -931
- package/dist/icons/site-logo-icon/SiteLogoIcon.stories.tsx +137 -1104
- package/dist/icons/skip-backward-icon/SkipBackwardIcon.stories.tsx +172 -982
- package/dist/icons/skip-forward-icon/SkipForwardIcon.stories.tsx +164 -983
- package/dist/icons/sparkles-soft-icon/SparklesSoftIcon.stories.tsx +105 -958
- package/dist/icons/spinner-gradient-icon/SpinnerGradientIcon.stories.tsx +158 -580
- package/dist/icons/spinner-gradient-icon/index.tsx +6 -1
- package/dist/icons/spinner-solid-icon/SpinnerSolidIcon.stories.tsx +158 -587
- package/dist/icons/spinner-solid-icon/index.tsx +6 -1
- package/dist/icons/spinner-solid-neutral-icon/SpinnerSolidINeutralcon.stories.tsx +146 -682
- package/dist/icons/spinner-solid-neutral-icon/index.tsx +1 -1
- package/dist/icons/star-icon/StarIcon.stories.tsx +124 -904
- package/dist/icons/store-coin-icon/StoreCoinIcon.stories.tsx +112 -964
- package/dist/icons/suggestion-icon/SuggestionIcon.stories.tsx +116 -852
- package/dist/icons/sun-icon/SunIcon.stories.tsx +120 -831
- package/dist/icons/text-color-icon/TextColorIcon.stories.tsx +116 -950
- package/dist/icons/text-indicator-icon/TextIndicatorIcon.stories.tsx +123 -980
- package/dist/icons/threads-icon/ThreadsIcon.stories.tsx +156 -1427
- package/dist/icons/tick-circle-icon/TickCircleIcon.stories.tsx +146 -1142
- package/dist/icons/tick-icon/TickIcon.stories.tsx +145 -1276
- package/dist/icons/trash-icon/TrashIcon.stories.tsx +108 -933
- package/dist/icons/twitter-x-icon/TwitterXIcon.stories.tsx +157 -1402
- package/dist/icons/upload-icon/UploadIcon.stories.tsx +115 -889
- package/dist/icons/vertical-menu-icon/VerticalMenuIcon.stories.tsx +118 -984
- package/dist/icons/video-play-list-icon/VideoPlaylistIcon.stories.tsx +125 -1049
- package/dist/icons/voice-playing-icon/VoicePlayingIcon.stories.tsx +123 -1356
- package/dist/icons/volume-full-icon/VolumeFullIcon.stories.tsx +110 -1171
- package/dist/icons/volume-half-icon/VolumeHalfIcon.stories.tsx +112 -1093
- package/dist/icons/volume-off-icon/VolumeOffIcon.stories.tsx +115 -1087
- package/dist/icons/warning-icon/WarningIcon.stories.tsx +122 -1046
- package/dist/icons/youtube-icon/YoutubeIcon.stories.tsx +161 -936
- package/dist/index.cjs +84 -84
- package/dist/index.js +84 -84
- package/dist/styles/aural-all-theme.css +1222 -0
- package/dist/styles/{aural-theme.css → aural-dark-theme.css} +15 -3
- package/dist/styles/aural-light-theme.css +1047 -0
- package/package.json +1 -1
|
@@ -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-white">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-
|
|
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="
|
|
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-sm text-white/80">
|
|
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,660 +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
|
-
Low (40%)
|
|
166
|
-
</Button>
|
|
167
|
-
<Button
|
|
168
|
-
size="sm"
|
|
169
|
-
variant={activeOverlay === "medium" ? "primary" : "outline"}
|
|
170
|
-
onClick={() =>
|
|
171
|
-
setActiveOverlay(activeOverlay === "medium" ? null : "medium")
|
|
172
|
-
}
|
|
173
|
-
className="w-full"
|
|
174
|
-
>
|
|
175
|
-
Medium (60%)
|
|
176
|
-
</Button>
|
|
177
|
-
<Button
|
|
178
|
-
size="sm"
|
|
179
|
-
variant={activeOverlay === "high" ? "primary" : "outline"}
|
|
180
|
-
onClick={() =>
|
|
181
|
-
setActiveOverlay(activeOverlay === "high" ? null : "high")
|
|
182
|
-
}
|
|
183
|
-
className="w-full"
|
|
184
|
-
>
|
|
185
|
-
High (80%)
|
|
186
|
-
</Button>
|
|
187
|
-
<Button
|
|
188
|
-
size="sm"
|
|
189
|
-
variant={activeOverlay === "none" ? "primary" : "outline"}
|
|
190
|
-
onClick={() =>
|
|
191
|
-
setActiveOverlay(activeOverlay === "none" ? null : "none")
|
|
192
|
-
}
|
|
193
|
-
className="w-full"
|
|
194
|
-
>
|
|
195
|
-
None (100%)
|
|
196
|
-
</Button>
|
|
197
|
-
</div>
|
|
198
|
-
{activeOverlay && (
|
|
199
|
-
<Button
|
|
200
|
-
size="sm"
|
|
201
|
-
variant="secondary"
|
|
202
|
-
onClick={() => setActiveOverlay(null)}
|
|
203
|
-
className="w-full"
|
|
204
|
-
>
|
|
205
|
-
Clear Overlay
|
|
206
|
-
</Button>
|
|
207
|
-
)}
|
|
208
|
-
</div>
|
|
209
|
-
</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
|
+
}
|
|
210
115
|
|
|
211
|
-
|
|
212
|
-
{activeOverlay === "low" && (
|
|
213
|
-
<Overlay opacity="low">
|
|
214
|
-
<div className="max-w-md rounded-lg border border-white/20 bg-white/10 p-6 backdrop-blur-sm">
|
|
215
|
-
<h3 className="mb-2 text-lg font-semibold text-white">
|
|
216
|
-
Low Opacity Overlay
|
|
217
|
-
</h3>
|
|
218
|
-
<p className="text-sm text-white/70">
|
|
219
|
-
40% background dimming - Background remains quite visible
|
|
220
|
-
</p>
|
|
221
|
-
</div>
|
|
222
|
-
</Overlay>
|
|
223
|
-
)}
|
|
116
|
+
const hasActive = activeOpacity || activeGlass || activeNoise
|
|
224
117
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
<p className="text-sm text-white/70">
|
|
232
|
-
60% background dimming - Balanced visibility
|
|
233
|
-
</p>
|
|
234
|
-
</div>
|
|
235
|
-
</Overlay>
|
|
236
|
-
)}
|
|
237
|
-
|
|
238
|
-
{activeOverlay === "high" && (
|
|
239
|
-
<Overlay opacity="high">
|
|
240
|
-
<div className="max-w-md rounded-lg border border-white/20 bg-white/10 p-6 backdrop-blur-sm">
|
|
241
|
-
<h3 className="mb-2 text-lg font-semibold text-white">
|
|
242
|
-
High Opacity Overlay
|
|
243
|
-
</h3>
|
|
244
|
-
<p className="text-sm text-white/70">
|
|
245
|
-
80% background dimming - Strong focus on overlay content
|
|
246
|
-
</p>
|
|
247
|
-
</div>
|
|
248
|
-
</Overlay>
|
|
249
|
-
)}
|
|
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
|
|
250
124
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
<p className="text-sm text-white/70">
|
|
258
|
-
100% background coverage - Complete background blocking
|
|
259
|
-
</p>
|
|
260
|
-
</div>
|
|
261
|
-
</Overlay>
|
|
262
|
-
)}
|
|
263
|
-
</div>
|
|
264
|
-
)
|
|
265
|
-
},
|
|
266
|
-
parameters: {
|
|
267
|
-
docs: {
|
|
268
|
-
description: {
|
|
269
|
-
story:
|
|
270
|
-
"Interactive demonstration of different opacity levels showing how background visibility changes from 40% to 100% coverage.",
|
|
271
|
-
},
|
|
272
|
-
},
|
|
273
|
-
},
|
|
274
|
-
}
|
|
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
|
|
275
131
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
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
|
|
280
138
|
|
|
281
139
|
return (
|
|
282
|
-
<div className="
|
|
283
|
-
<
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
Glass Effect Variants
|
|
290
|
-
</h3>
|
|
291
|
-
<div className="space-y-2">
|
|
292
|
-
<Button
|
|
293
|
-
size="sm"
|
|
294
|
-
variant={activeGlass === "none" ? "primary" : "outline"}
|
|
295
|
-
onClick={() =>
|
|
296
|
-
setActiveGlass(activeGlass === "none" ? null : "none")
|
|
297
|
-
}
|
|
298
|
-
className="w-full"
|
|
299
|
-
>
|
|
300
|
-
No Glass
|
|
301
|
-
</Button>
|
|
302
|
-
<Button
|
|
303
|
-
size="sm"
|
|
304
|
-
variant={activeGlass === "low" ? "primary" : "outline"}
|
|
305
|
-
onClick={() =>
|
|
306
|
-
setActiveGlass(activeGlass === "low" ? null : "low")
|
|
307
|
-
}
|
|
308
|
-
className="w-full"
|
|
309
|
-
>
|
|
310
|
-
Low Blur
|
|
311
|
-
</Button>
|
|
312
|
-
<Button
|
|
313
|
-
size="sm"
|
|
314
|
-
variant={activeGlass === "medium" ? "primary" : "outline"}
|
|
315
|
-
onClick={() =>
|
|
316
|
-
setActiveGlass(activeGlass === "medium" ? null : "medium")
|
|
317
|
-
}
|
|
318
|
-
className="w-full"
|
|
319
|
-
>
|
|
320
|
-
Medium Blur
|
|
321
|
-
</Button>
|
|
322
|
-
<Button
|
|
323
|
-
size="sm"
|
|
324
|
-
variant={activeGlass === "high" ? "primary" : "outline"}
|
|
325
|
-
onClick={() =>
|
|
326
|
-
setActiveGlass(activeGlass === "high" ? null : "high")
|
|
327
|
-
}
|
|
328
|
-
className="w-full"
|
|
329
|
-
>
|
|
330
|
-
High Blur
|
|
331
|
-
</Button>
|
|
332
|
-
</div>
|
|
333
|
-
{activeGlass && (
|
|
334
|
-
<Button
|
|
335
|
-
size="sm"
|
|
336
|
-
variant="secondary"
|
|
337
|
-
onClick={() => setActiveGlass(null)}
|
|
338
|
-
className="w-full"
|
|
339
|
-
>
|
|
340
|
-
Clear Overlay
|
|
341
|
-
</Button>
|
|
342
|
-
)}
|
|
343
|
-
</div>
|
|
344
|
-
</div>
|
|
345
|
-
|
|
346
|
-
{/* Glass Effect Overlays */}
|
|
347
|
-
{activeGlass === "none" && (
|
|
348
|
-
<Overlay opacity="medium" glass="none">
|
|
349
|
-
<div className="max-w-md rounded-lg border border-white/20 bg-white/10 p-6">
|
|
350
|
-
<h3 className="mb-2 text-lg font-semibold text-white">
|
|
351
|
-
No Glass Effect
|
|
352
|
-
</h3>
|
|
353
|
-
<p className="text-sm text-white/70">
|
|
354
|
-
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
|
|
355
147
|
</p>
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
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>
|
|
359
165
|
|
|
360
|
-
|
|
361
|
-
<Overlay opacity="medium" glass="low">
|
|
362
|
-
<div className="max-w-md rounded-lg border border-white/20 bg-white/10 p-6">
|
|
363
|
-
<h3 className="mb-2 text-lg font-semibold text-white">
|
|
364
|
-
Low Glass Effect
|
|
365
|
-
</h3>
|
|
366
|
-
<p className="text-sm text-white/70">
|
|
367
|
-
Subtle backdrop blur for gentle glass effect
|
|
368
|
-
</p>
|
|
369
|
-
</div>
|
|
370
|
-
</Overlay>
|
|
371
|
-
)}
|
|
166
|
+
<div className="border-fm-divider-secondary border-t pt-4" />
|
|
372
167
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
<div className="max-w-md rounded-lg border border-white/20 bg-white/10 p-6">
|
|
376
|
-
<h3 className="mb-2 text-lg font-semibold text-white">
|
|
377
|
-
Medium Glass Effect
|
|
378
|
-
</h3>
|
|
379
|
-
<p className="text-sm text-white/70">
|
|
380
|
-
Balanced backdrop blur for modern glass aesthetics
|
|
168
|
+
<p className="text-fm-primary font-fm-brand text-fm-sm leading-fm-sm font-semibold tracking-widest uppercase">
|
|
169
|
+
Glass
|
|
381
170
|
</p>
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
)}
|
|
398
|
-
</div>
|
|
399
|
-
)
|
|
400
|
-
},
|
|
401
|
-
parameters: {
|
|
402
|
-
docs: {
|
|
403
|
-
description: {
|
|
404
|
-
story:
|
|
405
|
-
"Interactive demonstration of glass effect variants showing different levels of backdrop blur from none to high intensity.",
|
|
406
|
-
},
|
|
407
|
-
},
|
|
408
|
-
},
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
// 3. Loading States
|
|
412
|
-
export const LoadingStates: Story = {
|
|
413
|
-
render: () => {
|
|
414
|
-
const [loadingType, setLoadingType] = useState<string | null>(null)
|
|
415
|
-
|
|
416
|
-
const startLoading = (type: string) => {
|
|
417
|
-
setLoadingType(type)
|
|
418
|
-
setTimeout(() => setLoadingType(null), 3000)
|
|
419
|
-
}
|
|
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>
|
|
420
186
|
|
|
421
|
-
|
|
422
|
-
<div className="relative">
|
|
423
|
-
<BackgroundContent />
|
|
424
|
-
|
|
425
|
-
{/* Control Panel */}
|
|
426
|
-
<div className="fixed top-4 left-1/2 z-50 -translate-x-1/2 transform">
|
|
427
|
-
<div className="space-y-2 rounded-lg bg-black/80 p-4 backdrop-blur-sm">
|
|
428
|
-
<h3 className="text-center text-sm font-medium text-white">
|
|
429
|
-
Loading States
|
|
430
|
-
</h3>
|
|
431
|
-
<div className="flex gap-2">
|
|
432
|
-
<Button
|
|
433
|
-
size="sm"
|
|
434
|
-
onClick={() => startLoading("spinner")}
|
|
435
|
-
disabled={!!loadingType}
|
|
436
|
-
>
|
|
437
|
-
Spinner Loading
|
|
438
|
-
</Button>
|
|
439
|
-
<Button
|
|
440
|
-
size="sm"
|
|
441
|
-
onClick={() => startLoading("progress")}
|
|
442
|
-
disabled={!!loadingType}
|
|
443
|
-
>
|
|
444
|
-
Progress Loading
|
|
445
|
-
</Button>
|
|
446
|
-
<Button
|
|
447
|
-
size="sm"
|
|
448
|
-
onClick={() => startLoading("dots")}
|
|
449
|
-
disabled={!!loadingType}
|
|
450
|
-
>
|
|
451
|
-
Dots Loading
|
|
452
|
-
</Button>
|
|
453
|
-
</div>
|
|
454
|
-
</div>
|
|
455
|
-
</div>
|
|
187
|
+
<div className="border-fm-divider-secondary border-t pt-4" />
|
|
456
188
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
<Overlay opacity="high" glass="medium">
|
|
460
|
-
<div className="text-center text-white">
|
|
461
|
-
<div className="mx-auto mb-4 h-12 w-12 animate-spin rounded-full border-b-2 border-white"></div>
|
|
462
|
-
<h3 className="mb-2 text-lg font-medium">Loading...</h3>
|
|
463
|
-
<p className="text-white/70">
|
|
464
|
-
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
|
|
465
191
|
</p>
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
</div>
|
|
481
|
-
<p className="text-white/70">
|
|
482
|
-
65% complete - 3 of 5 files uploaded
|
|
483
|
-
</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
|
+
))}
|
|
484
206
|
</div>
|
|
485
|
-
</div>
|
|
486
|
-
</Overlay>
|
|
487
|
-
)}
|
|
488
207
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
</div>
|
|
503
|
-
<h3 className="mb-2 text-lg font-medium">Processing</h3>
|
|
504
|
-
<p className="text-white/70">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
|
+
)}
|
|
505
221
|
</div>
|
|
506
|
-
</
|
|
507
|
-
)}
|
|
508
|
-
</div>
|
|
509
|
-
)
|
|
510
|
-
},
|
|
511
|
-
parameters: {
|
|
512
|
-
docs: {
|
|
513
|
-
description: {
|
|
514
|
-
story:
|
|
515
|
-
"Common loading state patterns using overlays with different animations and progress indicators.",
|
|
516
|
-
},
|
|
517
|
-
},
|
|
518
|
-
},
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
// 4. Interactive Examples
|
|
522
|
-
export const InteractiveExamples: Story = {
|
|
523
|
-
render: () => {
|
|
524
|
-
const [showSearch, setShowSearch] = useState(false)
|
|
525
|
-
const [showSettings, setShowSettings] = useState(false)
|
|
526
|
-
const [showConfirm, setShowConfirm] = useState(false)
|
|
222
|
+
</div>
|
|
527
223
|
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
{/* Control Panel */}
|
|
533
|
-
<div className="fixed top-4 left-1/2 z-50 -translate-x-1/2 transform">
|
|
534
|
-
<div className="space-y-2 rounded-lg bg-black/80 p-4 backdrop-blur-sm">
|
|
535
|
-
<h3 className="text-center text-sm font-medium text-white">
|
|
536
|
-
Interactive Examples
|
|
537
|
-
</h3>
|
|
538
|
-
<div className="flex gap-2">
|
|
539
|
-
<Button size="sm" onClick={() => setShowSearch(true)}>
|
|
540
|
-
Search Modal
|
|
541
|
-
</Button>
|
|
542
|
-
<Button size="sm" onClick={() => setShowSettings(true)}>
|
|
543
|
-
Settings
|
|
544
|
-
</Button>
|
|
545
|
-
<Button
|
|
546
|
-
size="sm"
|
|
547
|
-
variant="primary"
|
|
548
|
-
onClick={() => setShowConfirm(true)}
|
|
549
|
-
>
|
|
550
|
-
Confirm Action
|
|
551
|
-
</Button>
|
|
224
|
+
{/* ── Preview stage ── */}
|
|
225
|
+
<div className="relative overflow-hidden rounded-2xl">
|
|
226
|
+
<div className="h-[32rem] overflow-hidden rounded-2xl">
|
|
227
|
+
<BackingContent />
|
|
552
228
|
</div>
|
|
553
|
-
</div>
|
|
554
|
-
</div>
|
|
555
229
|
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
<CrossIcon className="h-4 w-4" />
|
|
567
|
-
</button>
|
|
568
|
-
</div>
|
|
569
|
-
<div className="space-y-4">
|
|
570
|
-
<div className="relative">
|
|
571
|
-
<SearchIcon className="absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 text-white/60" />
|
|
572
|
-
<input
|
|
573
|
-
type="text"
|
|
574
|
-
placeholder="Search anything..."
|
|
575
|
-
className="w-full rounded-lg border border-white/20 bg-white/10 py-3 pr-4 pl-10 text-white placeholder-white/60 focus:border-white/40 focus:outline-none"
|
|
576
|
-
/>
|
|
577
|
-
</div>
|
|
578
|
-
<div className="space-y-2">
|
|
579
|
-
{["Recent searches", "Popular items", "Suggestions"].map(
|
|
580
|
-
(item, i) => (
|
|
581
|
-
<div
|
|
582
|
-
key={i}
|
|
583
|
-
className="flex items-center gap-3 rounded-lg p-3 hover:bg-white/10"
|
|
584
|
-
>
|
|
585
|
-
<SearchIcon className="h-4 w-4 text-white/60" />
|
|
586
|
-
<span className="text-white">{item}</span>
|
|
587
|
-
</div>
|
|
588
|
-
)
|
|
589
|
-
)}
|
|
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>
|
|
590
240
|
</div>
|
|
591
241
|
</div>
|
|
592
|
-
|
|
593
|
-
</Overlay>
|
|
594
|
-
)}
|
|
242
|
+
)}
|
|
595
243
|
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
<
|
|
620
|
-
|
|
621
|
-
<span className="text-
|
|
622
|
-
|
|
623
|
-
|
|
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>
|
|
624
279
|
</div>
|
|
625
|
-
))}
|
|
626
|
-
</div>
|
|
627
|
-
</div>
|
|
628
|
-
</Overlay>
|
|
629
|
-
)}
|
|
630
|
-
|
|
631
|
-
{/* Confirmation Modal */}
|
|
632
|
-
{showConfirm && (
|
|
633
|
-
<Overlay opacity="high" glass="high">
|
|
634
|
-
<div className="w-full max-w-sm rounded-lg border border-red-500/30 bg-red-900/20 p-6 backdrop-blur-sm">
|
|
635
|
-
<div className="text-center">
|
|
636
|
-
<AlertIcon className="mx-auto mb-4 h-12 w-12 text-red-400" />
|
|
637
|
-
<h3 className="mb-2 text-lg font-semibold text-white">
|
|
638
|
-
Confirm Action
|
|
639
|
-
</h3>
|
|
640
|
-
<p className="mb-6 text-sm text-white/70">
|
|
641
|
-
This action cannot be undone. Are you sure you want to
|
|
642
|
-
continue?
|
|
643
|
-
</p>
|
|
644
|
-
<div className="flex gap-3">
|
|
645
280
|
<Button
|
|
646
|
-
variant="outline"
|
|
647
|
-
size="sm"
|
|
648
|
-
onClick={() => setShowConfirm(false)}
|
|
649
|
-
className="flex-1"
|
|
650
|
-
>
|
|
651
|
-
Cancel
|
|
652
|
-
</Button>
|
|
653
|
-
<Button
|
|
654
|
-
variant="secondary"
|
|
655
281
|
size="sm"
|
|
656
|
-
|
|
657
|
-
|
|
282
|
+
variant="outline"
|
|
283
|
+
onClick={clearAll}
|
|
284
|
+
className="mt-4 w-full"
|
|
658
285
|
>
|
|
659
|
-
|
|
286
|
+
Dismiss
|
|
660
287
|
</Button>
|
|
661
288
|
</div>
|
|
662
|
-
</
|
|
663
|
-
</div>
|
|
664
|
-
</Overlay>
|
|
665
|
-
)}
|
|
666
|
-
</div>
|
|
667
|
-
)
|
|
668
|
-
},
|
|
669
|
-
parameters: {
|
|
670
|
-
docs: {
|
|
671
|
-
description: {
|
|
672
|
-
story:
|
|
673
|
-
"Interactive overlay examples including search modals, settings panels, and confirmation dialogs demonstrating real-world usage patterns.",
|
|
674
|
-
},
|
|
675
|
-
},
|
|
676
|
-
},
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
// 5. Noise Texture Variants
|
|
680
|
-
export const NoiseTextureVariants: Story = {
|
|
681
|
-
render: () => {
|
|
682
|
-
const [activeNoise, setActiveNoise] = useState<string | null>(null)
|
|
683
|
-
|
|
684
|
-
return (
|
|
685
|
-
<div className="relative">
|
|
686
|
-
<BackgroundContent />
|
|
687
|
-
|
|
688
|
-
{/* Control Panel */}
|
|
689
|
-
<div className="fixed bottom-4 left-1/2 z-50 -translate-x-1/2 transform">
|
|
690
|
-
<div className="space-y-2 rounded-lg bg-black/80 p-4 backdrop-blur-sm">
|
|
691
|
-
<h3 className="text-center text-sm font-medium text-white">
|
|
692
|
-
Noise Texture Variants
|
|
693
|
-
</h3>
|
|
694
|
-
<div className="flex gap-2">
|
|
695
|
-
<Button
|
|
696
|
-
size="sm"
|
|
697
|
-
variant={activeNoise === "none" ? "primary" : "outline"}
|
|
698
|
-
onClick={() =>
|
|
699
|
-
setActiveNoise(activeNoise === "none" ? null : "none")
|
|
700
|
-
}
|
|
701
|
-
>
|
|
702
|
-
No Noise
|
|
703
|
-
</Button>
|
|
704
|
-
<Button
|
|
705
|
-
size="sm"
|
|
706
|
-
variant={activeNoise === "low" ? "primary" : "outline"}
|
|
707
|
-
onClick={() =>
|
|
708
|
-
setActiveNoise(activeNoise === "low" ? null : "low")
|
|
709
|
-
}
|
|
710
|
-
>
|
|
711
|
-
Low Noise
|
|
712
|
-
</Button>
|
|
713
|
-
<Button
|
|
714
|
-
size="sm"
|
|
715
|
-
variant={activeNoise === "medium" ? "primary" : "outline"}
|
|
716
|
-
onClick={() =>
|
|
717
|
-
setActiveNoise(activeNoise === "medium" ? null : "medium")
|
|
718
|
-
}
|
|
719
|
-
>
|
|
720
|
-
Medium Noise
|
|
721
|
-
</Button>
|
|
722
|
-
<Button
|
|
723
|
-
size="sm"
|
|
724
|
-
variant={activeNoise === "high" ? "primary" : "outline"}
|
|
725
|
-
onClick={() =>
|
|
726
|
-
setActiveNoise(activeNoise === "high" ? null : "high")
|
|
727
|
-
}
|
|
728
|
-
>
|
|
729
|
-
High Noise
|
|
730
|
-
</Button>
|
|
731
|
-
</div>
|
|
732
|
-
{activeNoise && (
|
|
733
|
-
<Button
|
|
734
|
-
size="sm"
|
|
735
|
-
variant="secondary"
|
|
736
|
-
onClick={() => setActiveNoise(null)}
|
|
737
|
-
className="w-full"
|
|
738
|
-
>
|
|
739
|
-
Clear Overlay
|
|
740
|
-
</Button>
|
|
289
|
+
</Overlay>
|
|
741
290
|
)}
|
|
742
291
|
</div>
|
|
743
292
|
</div>
|
|
744
|
-
|
|
745
|
-
{/* Noise Overlays */}
|
|
746
|
-
{activeNoise === "none" && (
|
|
747
|
-
<Overlay opacity="medium" glass="low" noise="none">
|
|
748
|
-
<div className="max-w-md rounded-lg border border-white/20 bg-white/10 p-6 backdrop-blur-sm">
|
|
749
|
-
<h3 className="mb-2 text-lg font-semibold text-white">
|
|
750
|
-
No Noise Texture
|
|
751
|
-
</h3>
|
|
752
|
-
<p className="text-sm text-white/70">
|
|
753
|
-
Clean overlay without any texture patterns
|
|
754
|
-
</p>
|
|
755
|
-
</div>
|
|
756
|
-
</Overlay>
|
|
757
|
-
)}
|
|
758
|
-
|
|
759
|
-
{activeNoise === "low" && (
|
|
760
|
-
<Overlay opacity="medium" glass="low" noise="low">
|
|
761
|
-
<div className="max-w-md rounded-lg border border-white/20 bg-white/10 p-6 backdrop-blur-sm">
|
|
762
|
-
<h3 className="mb-2 text-lg font-semibold text-white">
|
|
763
|
-
Low Noise Texture
|
|
764
|
-
</h3>
|
|
765
|
-
<p className="text-sm text-white/70">
|
|
766
|
-
Subtle noise pattern for added visual interest
|
|
767
|
-
</p>
|
|
768
|
-
</div>
|
|
769
|
-
</Overlay>
|
|
770
|
-
)}
|
|
771
|
-
|
|
772
|
-
{activeNoise === "medium" && (
|
|
773
|
-
<Overlay opacity="medium" glass="low" noise="medium">
|
|
774
|
-
<div className="max-w-md rounded-lg border border-white/20 bg-white/10 p-6 backdrop-blur-sm">
|
|
775
|
-
<h3 className="mb-2 text-lg font-semibold text-white">
|
|
776
|
-
Medium Noise Texture
|
|
777
|
-
</h3>
|
|
778
|
-
<p className="text-sm text-white/70">
|
|
779
|
-
Balanced noise pattern for enhanced texture
|
|
780
|
-
</p>
|
|
781
|
-
</div>
|
|
782
|
-
</Overlay>
|
|
783
|
-
)}
|
|
784
|
-
|
|
785
|
-
{activeNoise === "high" && (
|
|
786
|
-
<Overlay opacity="medium" glass="low" noise="high">
|
|
787
|
-
<div className="max-w-md rounded-lg border border-white/20 bg-white/10 p-6 backdrop-blur-sm">
|
|
788
|
-
<h3 className="mb-2 text-lg font-semibold text-white">
|
|
789
|
-
High Noise Texture
|
|
790
|
-
</h3>
|
|
791
|
-
<p className="text-sm text-white/70">
|
|
792
|
-
Prominent noise pattern for dramatic effect
|
|
793
|
-
</p>
|
|
794
|
-
</div>
|
|
795
|
-
</Overlay>
|
|
796
|
-
)}
|
|
797
293
|
</div>
|
|
798
294
|
)
|
|
799
295
|
},
|
|
@@ -801,144 +297,282 @@ export const NoiseTextureVariants: Story = {
|
|
|
801
297
|
docs: {
|
|
802
298
|
description: {
|
|
803
299
|
story:
|
|
804
|
-
"
|
|
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.",
|
|
805
301
|
},
|
|
806
302
|
},
|
|
807
303
|
},
|
|
808
304
|
}
|
|
809
305
|
|
|
810
|
-
//
|
|
811
|
-
export const CombinedEffectsShowcase: Story = {
|
|
812
|
-
render: () => {
|
|
813
|
-
const [currentCombo, setCurrentCombo] = useState<string | null>(null)
|
|
306
|
+
// ─── 2. Showcase ──────────────────────────────────────────────────────────────
|
|
814
307
|
|
|
815
|
-
|
|
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 = [
|
|
816
317
|
{
|
|
817
|
-
id: "minimal",
|
|
818
318
|
name: "Minimal",
|
|
319
|
+
meta: "opacity: low · glass: none · noise: none",
|
|
819
320
|
opacity: "low",
|
|
820
321
|
glass: "none",
|
|
821
322
|
noise: "none",
|
|
323
|
+
backing: "bg-fm-surface-primary",
|
|
822
324
|
},
|
|
823
325
|
{
|
|
824
|
-
id: "subtle",
|
|
825
326
|
name: "Subtle",
|
|
327
|
+
meta: "opacity: medium · glass: low · noise: low",
|
|
826
328
|
opacity: "medium",
|
|
827
329
|
glass: "low",
|
|
828
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",
|
|
829
340
|
},
|
|
830
341
|
{
|
|
831
|
-
id: "balanced",
|
|
832
342
|
name: "Balanced",
|
|
343
|
+
meta: "opacity: medium · glass: medium · noise: medium",
|
|
833
344
|
opacity: "medium",
|
|
834
345
|
glass: "medium",
|
|
835
346
|
noise: "medium",
|
|
347
|
+
backing: "bg-fm-surface-secondary",
|
|
836
348
|
},
|
|
837
349
|
{
|
|
838
|
-
id: "dramatic",
|
|
839
350
|
name: "Dramatic",
|
|
351
|
+
meta: "opacity: high · glass: high · noise: low",
|
|
840
352
|
opacity: "high",
|
|
841
353
|
glass: "high",
|
|
842
354
|
noise: "low",
|
|
355
|
+
backing: "bg-fm-surface-primary",
|
|
843
356
|
},
|
|
844
357
|
{
|
|
845
|
-
|
|
846
|
-
|
|
358
|
+
name: "Textured",
|
|
359
|
+
meta: "opacity: high · glass: medium · noise: high",
|
|
847
360
|
opacity: "high",
|
|
848
|
-
glass: "
|
|
361
|
+
glass: "medium",
|
|
849
362
|
noise: "high",
|
|
363
|
+
backing: "bg-fm-surface-contrast",
|
|
850
364
|
},
|
|
851
365
|
] as const
|
|
852
366
|
|
|
853
367
|
return (
|
|
854
|
-
<div className="
|
|
855
|
-
<
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
</h3>
|
|
863
|
-
<div className="space-y-2">
|
|
864
|
-
{combinations.map((combo) => (
|
|
865
|
-
<Button
|
|
866
|
-
key={combo.id}
|
|
867
|
-
size="sm"
|
|
868
|
-
variant={currentCombo === combo.id ? "primary" : "outline"}
|
|
869
|
-
onClick={() =>
|
|
870
|
-
setCurrentCombo(currentCombo === combo.id ? null : combo.id)
|
|
871
|
-
}
|
|
872
|
-
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}`}
|
|
873
376
|
>
|
|
874
|
-
{
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
>
|
|
885
|
-
Clear Overlay
|
|
886
|
-
</Button>
|
|
887
|
-
)}
|
|
888
|
-
</div>
|
|
889
|
-
</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>
|
|
890
387
|
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
<div className="space-y-1 text-xs text-white/70">
|
|
899
|
-
<div>
|
|
900
|
-
Opacity:{" "}
|
|
901
|
-
{combinations.find((c) => c.id === currentCombo)?.opacity}
|
|
902
|
-
</div>
|
|
903
|
-
<div>
|
|
904
|
-
Glass:{" "}
|
|
905
|
-
{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
|
+
/>
|
|
906
395
|
</div>
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
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>
|
|
910
405
|
</div>
|
|
911
406
|
</div>
|
|
912
|
-
|
|
407
|
+
))}
|
|
913
408
|
</div>
|
|
914
|
-
)}
|
|
915
409
|
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
<
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
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
|
|
938
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
|
+
))}
|
|
939
527
|
</div>
|
|
940
|
-
|
|
941
|
-
|
|
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>
|
|
942
576
|
)}
|
|
943
577
|
</div>
|
|
944
578
|
)
|
|
@@ -947,7 +581,7 @@ export const CombinedEffectsShowcase: Story = {
|
|
|
947
581
|
docs: {
|
|
948
582
|
description: {
|
|
949
583
|
story:
|
|
950
|
-
"
|
|
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.",
|
|
951
585
|
},
|
|
952
586
|
},
|
|
953
587
|
},
|