@rdna/radiants 0.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/LICENSE +674 -0
- package/README.md +125 -0
- package/animations.css +68 -0
- package/assets/scrollbar-background.svg +9 -0
- package/base.css +144 -0
- package/dark.css +117 -0
- package/dist/chunk-SR2T7OEJ.mjs +46 -0
- package/dist/chunk-SR2T7OEJ.mjs.map +1 -0
- package/dist/components/core/index.d.mts +911 -0
- package/dist/components/core/index.mjs +2475 -0
- package/dist/components/core/index.mjs.map +1 -0
- package/dist/hooks/index.d.mts +22 -0
- package/dist/hooks/index.mjs +3 -0
- package/dist/hooks/index.mjs.map +1 -0
- package/dist/remotion/index.d.mts +252 -0
- package/dist/remotion/index.mjs +170 -0
- package/dist/remotion/index.mjs.map +1 -0
- package/dna.config.json +8 -0
- package/fonts/Joystix.woff2 +0 -0
- package/fonts/PixelCode-Black-Italic.woff2 +0 -0
- package/fonts/PixelCode-Black.woff2 +0 -0
- package/fonts/PixelCode-Bold-Italic.woff2 +0 -0
- package/fonts/PixelCode-Bold.woff2 +0 -0
- package/fonts/PixelCode-DemiBold-Italic.woff2 +0 -0
- package/fonts/PixelCode-DemiBold.woff2 +0 -0
- package/fonts/PixelCode-ExtraBlack-Italic.woff2 +0 -0
- package/fonts/PixelCode-ExtraBlack.woff2 +0 -0
- package/fonts/PixelCode-ExtraBold-Italic.woff2 +0 -0
- package/fonts/PixelCode-ExtraBold.woff2 +0 -0
- package/fonts/PixelCode-ExtraLight-Italic.woff2 +0 -0
- package/fonts/PixelCode-ExtraLight.woff2 +0 -0
- package/fonts/PixelCode-Italic.woff2 +0 -0
- package/fonts/PixelCode-Light-Italic.woff2 +0 -0
- package/fonts/PixelCode-Light.woff2 +0 -0
- package/fonts/PixelCode-Medium-Italic.woff2 +0 -0
- package/fonts/PixelCode-Medium.woff2 +0 -0
- package/fonts/PixelCode-Thin-Italic.woff2 +0 -0
- package/fonts/PixelCode-Thin.woff2 +0 -0
- package/fonts/PixelCode.woff2 +0 -0
- package/fonts.css +115 -0
- package/index.css +25 -0
- package/package.json +88 -0
- package/tokens.css +202 -0
- package/typography.css +175 -0
package/README.md
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# @rdna/radiants
|
|
2
|
+
|
|
3
|
+
Radiants theme package for DNA (Design Nexus Architecture) — a retro pixel aesthetic design system.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @rdna/radiants
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
### CSS Tokens
|
|
14
|
+
|
|
15
|
+
Import the theme tokens in your CSS:
|
|
16
|
+
|
|
17
|
+
```css
|
|
18
|
+
/* Import all tokens + base styles */
|
|
19
|
+
@import '@rdna/radiants';
|
|
20
|
+
|
|
21
|
+
/* Import dark mode support */
|
|
22
|
+
@import '@rdna/radiants/dark';
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Or import individual parts:
|
|
26
|
+
|
|
27
|
+
```css
|
|
28
|
+
@import '@rdna/radiants/tokens'; /* Tokens only */
|
|
29
|
+
@import '@rdna/radiants/typography'; /* Typography styles */
|
|
30
|
+
@import '@rdna/radiants/fonts'; /* Font declarations */
|
|
31
|
+
@import '@rdna/radiants/animations'; /* Animation utilities */
|
|
32
|
+
@import '@rdna/radiants/base'; /* Base element styles */
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### React Components
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
import { Button, Card, Badge } from '@rdna/radiants/components/core';
|
|
39
|
+
import { useToast } from '@rdna/radiants/components/core';
|
|
40
|
+
|
|
41
|
+
function App() {
|
|
42
|
+
return (
|
|
43
|
+
<Card>
|
|
44
|
+
<Badge variant="success">New</Badge>
|
|
45
|
+
<Button variant="primary">Click me</Button>
|
|
46
|
+
</Card>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Available Components
|
|
52
|
+
|
|
53
|
+
- **Layout**: Card, Divider
|
|
54
|
+
- **Actions**: Button, ContextMenu, DropdownMenu
|
|
55
|
+
- **Forms**: Input, TextArea, Select, Checkbox, Radio, Switch, Slider
|
|
56
|
+
- **Feedback**: Alert, Badge, Progress, Spinner, Toast, Tooltip
|
|
57
|
+
- **Overlays**: Dialog, Popover, Sheet, HelpPanel
|
|
58
|
+
- **Navigation**: Tabs, Breadcrumbs, Accordion
|
|
59
|
+
- **Specialty**: CountdownTimer, Web3ActionBar
|
|
60
|
+
|
|
61
|
+
### Hooks
|
|
62
|
+
|
|
63
|
+
```tsx
|
|
64
|
+
import { useMotion } from '@rdna/radiants/hooks';
|
|
65
|
+
|
|
66
|
+
const { duration, easing } = useMotion();
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Semantic Tokens
|
|
70
|
+
|
|
71
|
+
Use semantic token classes instead of hardcoded colors:
|
|
72
|
+
|
|
73
|
+
```tsx
|
|
74
|
+
// ✅ Do this
|
|
75
|
+
<div className="bg-surface-primary text-content-primary border-edge-primary">
|
|
76
|
+
|
|
77
|
+
// ❌ Not this
|
|
78
|
+
<div className="bg-[#FEF8E2] text-[#0F0E0C]">
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Token Categories
|
|
82
|
+
|
|
83
|
+
| Category | Examples | Purpose |
|
|
84
|
+
|----------|----------|---------|
|
|
85
|
+
| `surface-*` | `bg-surface-primary` | Backgrounds |
|
|
86
|
+
| `content-*` | `text-content-primary` | Text/foreground |
|
|
87
|
+
| `edge-*` | `border-edge-primary` | Borders/outlines |
|
|
88
|
+
| `action-*` | `bg-action-primary` | Interactive elements |
|
|
89
|
+
| `status-*` | `bg-status-success` | Feedback states |
|
|
90
|
+
|
|
91
|
+
## Dark Mode
|
|
92
|
+
|
|
93
|
+
Dark mode is automatic with `prefers-color-scheme`, or manually toggle with classes:
|
|
94
|
+
|
|
95
|
+
```html
|
|
96
|
+
<!-- Force dark -->
|
|
97
|
+
<html class="dark">
|
|
98
|
+
|
|
99
|
+
<!-- Force light -->
|
|
100
|
+
<html class="light">
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Fonts
|
|
104
|
+
|
|
105
|
+
This package includes:
|
|
106
|
+
- **Joystix Monospace** — Heading font
|
|
107
|
+
- **PixelCode** — Monospace/code font
|
|
108
|
+
|
|
109
|
+
**Mondwest** (body font) must be downloaded separately due to licensing:
|
|
110
|
+
|
|
111
|
+
1. Purchase/download from [Pangram Pangram](https://pangrampangram.com/products/bitmap-mondwest)
|
|
112
|
+
2. Place `Mondwest.woff2` and `Mondwest-Bold.woff2` in your project's fonts directory
|
|
113
|
+
3. The theme will fall back to system fonts if Mondwest is not available
|
|
114
|
+
|
|
115
|
+
## Requirements
|
|
116
|
+
|
|
117
|
+
- React 18+ or 19
|
|
118
|
+
- Tailwind CSS 4
|
|
119
|
+
- Next.js 14+ (optional)
|
|
120
|
+
|
|
121
|
+
## License
|
|
122
|
+
|
|
123
|
+
GPL-3.0 — See [LICENSE](./LICENSE) for details.
|
|
124
|
+
|
|
125
|
+
Note: The DNA specification itself is MIT licensed. This theme implementation is GPL-3.0.
|
package/animations.css
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/* =============================================================================
|
|
2
|
+
@dna/radiants - Animations
|
|
3
|
+
Animation keyframes and utility classes for the Radiants theme
|
|
4
|
+
============================================================================= */
|
|
5
|
+
|
|
6
|
+
/* ============================================================================
|
|
7
|
+
Keyframe Animations
|
|
8
|
+
============================================================================ */
|
|
9
|
+
|
|
10
|
+
@keyframes slide-in-right {
|
|
11
|
+
from {
|
|
12
|
+
transform: translateX(100%);
|
|
13
|
+
}
|
|
14
|
+
to {
|
|
15
|
+
transform: translateX(0);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
@keyframes fadeIn {
|
|
20
|
+
from {
|
|
21
|
+
opacity: 0;
|
|
22
|
+
}
|
|
23
|
+
to {
|
|
24
|
+
opacity: 1;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
@keyframes scaleIn {
|
|
29
|
+
from {
|
|
30
|
+
opacity: 0;
|
|
31
|
+
transform: scale(0.95);
|
|
32
|
+
}
|
|
33
|
+
to {
|
|
34
|
+
opacity: 1;
|
|
35
|
+
transform: scale(1);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
@keyframes slideIn {
|
|
40
|
+
from {
|
|
41
|
+
opacity: 0;
|
|
42
|
+
transform: translateX(100%);
|
|
43
|
+
}
|
|
44
|
+
to {
|
|
45
|
+
opacity: 1;
|
|
46
|
+
transform: translateX(0);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/* ============================================================================
|
|
51
|
+
Animation Utility Classes
|
|
52
|
+
============================================================================ */
|
|
53
|
+
|
|
54
|
+
.animate-slide-in-right {
|
|
55
|
+
animation: slide-in-right 0.2s ease-out forwards;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.animate-fadeIn {
|
|
59
|
+
animation: fadeIn 0.15s ease-out forwards;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.animate-scaleIn {
|
|
63
|
+
animation: scaleIn 0.15s ease-out forwards;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.animate-slideIn {
|
|
67
|
+
animation: slideIn 0.2s ease-out forwards;
|
|
68
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<svg width="8" height="341" viewBox="0 0 8 341" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
2
|
+
<rect width="7" height="340.706" transform="translate(0.5 0.294922)" fill="url(#pattern0_162_293)"/>
|
|
3
|
+
<defs>
|
|
4
|
+
<pattern id="pattern0_162_293" patternContentUnits="objectBoundingBox" width="1" height="0.610498">
|
|
5
|
+
<use xlink:href="#image0_162_293" transform="scale(0.0714286 0.00146754)"/>
|
|
6
|
+
</pattern>
|
|
7
|
+
<image id="image0_162_293" width="14" height="416" preserveAspectRatio="none" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAGgAQMAAACg58S4AAAABlBMVEUAAAAAAAClZ7nPAAAAAnRSTlMATX7+8BUAAAAaSURBVDjLYzhwAIR4eIBolD3KHmWPsocTGwCp5UuQtb1x6QAAAABJRU5ErkJggg=="/>
|
|
8
|
+
</defs>
|
|
9
|
+
</svg>
|
package/base.css
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/* =============================================================================
|
|
2
|
+
@dna/radiants - Base Styles
|
|
3
|
+
Box-sizing reset, html/body defaults, selection styles, and scrollbar CSS
|
|
4
|
+
============================================================================= */
|
|
5
|
+
|
|
6
|
+
/* ============================================================================
|
|
7
|
+
Box-Sizing Reset
|
|
8
|
+
============================================================================ */
|
|
9
|
+
|
|
10
|
+
* {
|
|
11
|
+
box-sizing: border-box;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/* ============================================================================
|
|
15
|
+
HTML/Body Base Styles
|
|
16
|
+
============================================================================ */
|
|
17
|
+
|
|
18
|
+
html,
|
|
19
|
+
body {
|
|
20
|
+
margin: 0;
|
|
21
|
+
padding: 0;
|
|
22
|
+
width: 100%;
|
|
23
|
+
height: 100%;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
body {
|
|
27
|
+
font-family: 'Joystix Monospace', monospace;
|
|
28
|
+
font-size: clamp(1rem, 1vw, 1.125rem);
|
|
29
|
+
background-color: var(--color-sun-yellow);
|
|
30
|
+
color: var(--color-black);
|
|
31
|
+
-webkit-font-smoothing: antialiased;
|
|
32
|
+
-moz-osx-font-smoothing: grayscale;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/* ============================================================================
|
|
36
|
+
Text Selection
|
|
37
|
+
============================================================================ */
|
|
38
|
+
|
|
39
|
+
::selection {
|
|
40
|
+
background: var(--color-sun-yellow);
|
|
41
|
+
color: var(--color-black);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
::-moz-selection {
|
|
45
|
+
background: var(--color-sun-yellow);
|
|
46
|
+
color: var(--color-black);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/* ============================================================================
|
|
50
|
+
Custom Scrollbar Styles - Mac Compatible
|
|
51
|
+
|
|
52
|
+
Retro-style webkit scrollbar matching the design system.
|
|
53
|
+
Uses CSS variables from tokens.css for consistency.
|
|
54
|
+
============================================================================ */
|
|
55
|
+
|
|
56
|
+
/* Force scrollbars to always be visible - make wider for padding effect */
|
|
57
|
+
::-webkit-scrollbar {
|
|
58
|
+
width: 1.75rem; /* Wider invisible container for padding effect */
|
|
59
|
+
height: 0px;
|
|
60
|
+
-webkit-appearance: none;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/* Only show horizontal scrollbar when actually needed */
|
|
64
|
+
::-webkit-scrollbar:horizontal {
|
|
65
|
+
height: 0.5rem;
|
|
66
|
+
display: none !important;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/* Track - narrow and centered within wider scrollbar */
|
|
70
|
+
::-webkit-scrollbar-track {
|
|
71
|
+
background: url('./assets/scrollbar-background.svg') center center / 0.5rem auto repeat-y;
|
|
72
|
+
background-position: bottom;
|
|
73
|
+
background-size: 45%;
|
|
74
|
+
-webkit-appearance: none;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/* Hide horizontal scrollbar track completely */
|
|
78
|
+
::-webkit-scrollbar-track:horizontal {
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/* Thumb - using border/background-clip method for inset effect */
|
|
82
|
+
::-webkit-scrollbar-thumb {
|
|
83
|
+
background: var(--color-warm-cloud);
|
|
84
|
+
border: 0.375rem solid transparent; /* Transparent border creates padding */
|
|
85
|
+
border-radius: var(--radius-md);
|
|
86
|
+
-webkit-appearance: none;
|
|
87
|
+
min-height: 2.25rem;
|
|
88
|
+
background-clip: padding-box; /* This is the magic - clips background to content area only */
|
|
89
|
+
/* Add outline around the green area using inset box-shadow */
|
|
90
|
+
box-shadow: inset 0 0 0 1px var(--color-black);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/* Thumb hover state */
|
|
94
|
+
::-webkit-scrollbar-thumb:hover {
|
|
95
|
+
background: var(--color-sun-yellow);
|
|
96
|
+
border: 0.375rem solid transparent;
|
|
97
|
+
background-clip: padding-box;
|
|
98
|
+
box-shadow: inset 0 0 0 1px var(--color-black);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/* Thumb active state */
|
|
102
|
+
::-webkit-scrollbar-thumb:active {
|
|
103
|
+
background: var(--color-sun-yellow);
|
|
104
|
+
border: 0.375rem solid transparent;
|
|
105
|
+
background-clip: padding-box;
|
|
106
|
+
box-shadow: inset 0 0 0 1px var(--color-black);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/* Scrollbar buttons (arrows) - Hide all buttons */
|
|
110
|
+
::-webkit-scrollbar-button {
|
|
111
|
+
display: none !important; /* Hide all arrow buttons */
|
|
112
|
+
height: 0 !important;
|
|
113
|
+
width: 0 !important;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/* Corner where horizontal and vertical scrollbars meet */
|
|
117
|
+
::-webkit-scrollbar-corner {
|
|
118
|
+
display: none !important; /* Hide the corner completely */
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/* ============================================================================
|
|
122
|
+
Scrollable Container Utility Classes
|
|
123
|
+
|
|
124
|
+
Apply .custom-scrollbar to any scrollable element for styled scrollbars
|
|
125
|
+
============================================================================ */
|
|
126
|
+
|
|
127
|
+
.custom-scrollbar {
|
|
128
|
+
scrollbar-width: thin;
|
|
129
|
+
scrollbar-color: var(--color-warm-cloud) var(--color-sun-yellow);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/* For elements that need a dark-themed scrollbar */
|
|
133
|
+
.custom-scrollbar-dark::-webkit-scrollbar-track {
|
|
134
|
+
background: var(--color-black);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.custom-scrollbar-dark::-webkit-scrollbar-thumb {
|
|
138
|
+
background: var(--color-warm-cloud);
|
|
139
|
+
box-shadow: inset 0 0 0 1px var(--color-sun-yellow);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.custom-scrollbar-dark::-webkit-scrollbar-thumb:hover {
|
|
143
|
+
background: var(--color-sun-yellow);
|
|
144
|
+
}
|
package/dark.css
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/* =============================================================================
|
|
2
|
+
@dna/radiants - Dark Mode Overrides
|
|
3
|
+
Inverted semantic tokens for dark theme
|
|
4
|
+
============================================================================= */
|
|
5
|
+
|
|
6
|
+
/* =============================================================================
|
|
7
|
+
Dark Mode Class Selector
|
|
8
|
+
Apply via: <html class="dark"> or <body class="dark">
|
|
9
|
+
============================================================================= */
|
|
10
|
+
|
|
11
|
+
.dark {
|
|
12
|
+
/* ============================================
|
|
13
|
+
SURFACE TOKENS - Inverted for dark backgrounds
|
|
14
|
+
============================================ */
|
|
15
|
+
|
|
16
|
+
--color-surface-primary: var(--color-black);
|
|
17
|
+
--color-surface-secondary: var(--color-warm-cloud);
|
|
18
|
+
--color-surface-tertiary: #3D2E1A; /* Muted sunset-fuzz for dark - WCAG AA compliant */
|
|
19
|
+
--color-surface-elevated: #1A1918; /* Slightly lighter than black for depth */
|
|
20
|
+
--color-surface-muted: #252422; /* Muted dark surface */
|
|
21
|
+
|
|
22
|
+
/* ============================================
|
|
23
|
+
CONTENT TOKENS - Inverted for dark backgrounds
|
|
24
|
+
============================================ */
|
|
25
|
+
|
|
26
|
+
--color-content-primary: var(--color-warm-cloud);
|
|
27
|
+
--color-content-secondary: var(--color-warm-cloud); /* Base for opacity modifiers: text-content-secondary/70 */
|
|
28
|
+
--color-content-inverted: var(--color-black);
|
|
29
|
+
--color-content-muted: rgba(254, 248, 226, 0.6); /* warm-cloud at 60% */
|
|
30
|
+
--color-content-link: var(--color-sky-blue); /* Links stay sky-blue for contrast */
|
|
31
|
+
|
|
32
|
+
/* ============================================
|
|
33
|
+
EDGE TOKENS - Adjusted for dark backgrounds
|
|
34
|
+
============================================ */
|
|
35
|
+
|
|
36
|
+
--color-edge-primary: var(--color-warm-cloud);
|
|
37
|
+
--color-edge-muted: rgba(254, 248, 226, 0.2); /* warm-cloud at 20% */
|
|
38
|
+
--color-edge-focus: var(--color-sun-yellow); /* Focus stays yellow for visibility */
|
|
39
|
+
|
|
40
|
+
/* ============================================
|
|
41
|
+
ACTION TOKENS - Preserved for retro aesthetic
|
|
42
|
+
High-contrast action colors work on dark backgrounds
|
|
43
|
+
============================================ */
|
|
44
|
+
|
|
45
|
+
--color-action-primary: var(--color-sun-yellow);
|
|
46
|
+
--color-action-secondary: var(--color-warm-cloud);
|
|
47
|
+
--color-action-destructive: var(--color-sun-red); /* Same in dark - high contrast on dark bg */
|
|
48
|
+
--color-action-accent: var(--color-sunset-fuzz);
|
|
49
|
+
|
|
50
|
+
/* ============================================
|
|
51
|
+
BOX SHADOWS - Adjusted for dark mode
|
|
52
|
+
Using warm-cloud for retro lift effect on dark
|
|
53
|
+
============================================ */
|
|
54
|
+
|
|
55
|
+
--shadow-btn: 0 1px 0 0 var(--color-warm-cloud);
|
|
56
|
+
--shadow-btn-hover: 0 3px 0 0 var(--color-warm-cloud);
|
|
57
|
+
--shadow-card: 2px 2px 0 0 rgba(254, 248, 226, 0.3);
|
|
58
|
+
--shadow-card-lg: 4px 4px 0 0 rgba(254, 248, 226, 0.3);
|
|
59
|
+
--shadow-inner: inset 0 0 0 1px var(--color-warm-cloud);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/* =============================================================================
|
|
63
|
+
System Preference Fallback
|
|
64
|
+
Auto-applies dark mode when user prefers dark color scheme
|
|
65
|
+
The :not(.light) allows explicit light mode override
|
|
66
|
+
============================================================================= */
|
|
67
|
+
|
|
68
|
+
@media (prefers-color-scheme: dark) {
|
|
69
|
+
:root:not(.light) {
|
|
70
|
+
/* ============================================
|
|
71
|
+
SURFACE TOKENS - Inverted for dark backgrounds
|
|
72
|
+
============================================ */
|
|
73
|
+
|
|
74
|
+
--color-surface-primary: var(--color-black);
|
|
75
|
+
--color-surface-secondary: var(--color-warm-cloud);
|
|
76
|
+
--color-surface-tertiary: #3D2E1A; /* Muted sunset-fuzz for dark - WCAG AA compliant */
|
|
77
|
+
--color-surface-elevated: #1A1918;
|
|
78
|
+
--color-surface-muted: #252422;
|
|
79
|
+
|
|
80
|
+
/* ============================================
|
|
81
|
+
CONTENT TOKENS - Inverted for dark backgrounds
|
|
82
|
+
============================================ */
|
|
83
|
+
|
|
84
|
+
--color-content-primary: var(--color-warm-cloud);
|
|
85
|
+
--color-content-secondary: var(--color-warm-cloud); /* Base for opacity modifiers: text-content-secondary/70 */
|
|
86
|
+
--color-content-inverted: var(--color-black);
|
|
87
|
+
--color-content-muted: rgba(254, 248, 226, 0.6);
|
|
88
|
+
--color-content-link: var(--color-sky-blue);
|
|
89
|
+
|
|
90
|
+
/* ============================================
|
|
91
|
+
EDGE TOKENS - Adjusted for dark backgrounds
|
|
92
|
+
============================================ */
|
|
93
|
+
|
|
94
|
+
--color-edge-primary: var(--color-warm-cloud);
|
|
95
|
+
--color-edge-muted: rgba(254, 248, 226, 0.2);
|
|
96
|
+
--color-edge-focus: var(--color-sun-yellow);
|
|
97
|
+
|
|
98
|
+
/* ============================================
|
|
99
|
+
ACTION TOKENS - Preserved for retro aesthetic
|
|
100
|
+
============================================ */
|
|
101
|
+
|
|
102
|
+
--color-action-primary: var(--color-sun-yellow);
|
|
103
|
+
--color-action-secondary: var(--color-warm-cloud);
|
|
104
|
+
--color-action-destructive: var(--color-sun-red); /* Same in dark - high contrast on dark bg */
|
|
105
|
+
--color-action-accent: var(--color-sunset-fuzz);
|
|
106
|
+
|
|
107
|
+
/* ============================================
|
|
108
|
+
BOX SHADOWS - Adjusted for dark mode
|
|
109
|
+
============================================ */
|
|
110
|
+
|
|
111
|
+
--shadow-btn: 0 1px 0 0 var(--color-warm-cloud);
|
|
112
|
+
--shadow-btn-hover: 0 3px 0 0 var(--color-warm-cloud);
|
|
113
|
+
--shadow-card: 2px 2px 0 0 rgba(254, 248, 226, 0.3);
|
|
114
|
+
--shadow-card-lg: 4px 4px 0 0 rgba(254, 248, 226, 0.3);
|
|
115
|
+
--shadow-inner: inset 0 0 0 1px var(--color-warm-cloud);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
|
|
3
|
+
// hooks/useModalBehavior.ts
|
|
4
|
+
function useEscapeKey(isActive, onEscape) {
|
|
5
|
+
useEffect(() => {
|
|
6
|
+
if (!isActive) return;
|
|
7
|
+
const handleEscape = (e) => {
|
|
8
|
+
if (e.key === "Escape") {
|
|
9
|
+
onEscape();
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
document.addEventListener("keydown", handleEscape);
|
|
13
|
+
return () => document.removeEventListener("keydown", handleEscape);
|
|
14
|
+
}, [isActive, onEscape]);
|
|
15
|
+
}
|
|
16
|
+
function useClickOutside(isActive, refs, onClickOutside) {
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
if (!isActive) return;
|
|
19
|
+
const handleClickOutside = (e) => {
|
|
20
|
+
const clickedOutsideAll = refs.every(
|
|
21
|
+
(ref) => ref.current && !ref.current.contains(e.target)
|
|
22
|
+
);
|
|
23
|
+
if (clickedOutsideAll) {
|
|
24
|
+
onClickOutside();
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
28
|
+
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
29
|
+
}, [isActive, refs, onClickOutside]);
|
|
30
|
+
}
|
|
31
|
+
function useLockBodyScroll(isActive) {
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
if (isActive) {
|
|
34
|
+
document.body.style.overflow = "hidden";
|
|
35
|
+
} else {
|
|
36
|
+
document.body.style.overflow = "";
|
|
37
|
+
}
|
|
38
|
+
return () => {
|
|
39
|
+
document.body.style.overflow = "";
|
|
40
|
+
};
|
|
41
|
+
}, [isActive]);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export { useClickOutside, useEscapeKey, useLockBodyScroll };
|
|
45
|
+
//# sourceMappingURL=chunk-SR2T7OEJ.mjs.map
|
|
46
|
+
//# sourceMappingURL=chunk-SR2T7OEJ.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../hooks/useModalBehavior.ts"],"names":[],"mappings":";;;AAOO,SAAS,YAAA,CAAa,UAAmB,QAAA,EAA4B;AAC1E,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,QAAA,EAAU;AAEf,IAAA,MAAM,YAAA,GAAe,CAAC,CAAA,KAAqB;AACzC,MAAA,IAAI,CAAA,CAAE,QAAQ,QAAA,EAAU;AACtB,QAAA,QAAA,EAAS;AAAA,MACX;AAAA,IACF,CAAA;AAEA,IAAA,QAAA,CAAS,gBAAA,CAAiB,WAAW,YAAY,CAAA;AACjD,IAAA,OAAO,MAAM,QAAA,CAAS,mBAAA,CAAoB,SAAA,EAAW,YAAY,CAAA;AAAA,EACnE,CAAA,EAAG,CAAC,QAAA,EAAU,QAAQ,CAAC,CAAA;AACzB;AAQO,SAAS,eAAA,CACd,QAAA,EACA,IAAA,EACA,cAAA,EACM;AACN,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,QAAA,EAAU;AAEf,IAAA,MAAM,kBAAA,GAAqB,CAAC,CAAA,KAAkB;AAC5C,MAAA,MAAM,oBAAoB,IAAA,CAAK,KAAA;AAAA,QAC7B,CAAC,QAAQ,GAAA,CAAI,OAAA,IAAW,CAAC,GAAA,CAAI,OAAA,CAAQ,QAAA,CAAS,CAAA,CAAE,MAAc;AAAA,OAChE;AACA,MAAA,IAAI,iBAAA,EAAmB;AACrB,QAAA,cAAA,EAAe;AAAA,MACjB;AAAA,IACF,CAAA;AAEA,IAAA,QAAA,CAAS,gBAAA,CAAiB,aAAa,kBAAkB,CAAA;AACzD,IAAA,OAAO,MAAM,QAAA,CAAS,mBAAA,CAAoB,WAAA,EAAa,kBAAkB,CAAA;AAAA,EAC3E,CAAA,EAAG,CAAC,QAAA,EAAU,IAAA,EAAM,cAAc,CAAC,CAAA;AACrC;AAMO,SAAS,kBAAkB,QAAA,EAAyB;AACzD,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,QAAA,EAAU;AACZ,MAAA,QAAA,CAAS,IAAA,CAAK,MAAM,QAAA,GAAW,QAAA;AAAA,IACjC,CAAA,MAAO;AACL,MAAA,QAAA,CAAS,IAAA,CAAK,MAAM,QAAA,GAAW,EAAA;AAAA,IACjC;AACA,IAAA,OAAO,MAAM;AACX,MAAA,QAAA,CAAS,IAAA,CAAK,MAAM,QAAA,GAAW,EAAA;AAAA,IACjC,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,QAAQ,CAAC,CAAA;AACf","file":"chunk-SR2T7OEJ.mjs","sourcesContent":["import { useEffect, RefObject } from 'react';\n\n/**\n * Hook to handle escape key press to close modals/overlays\n * @param isActive - Whether the modal is currently open\n * @param onEscape - Callback to close the modal\n */\nexport function useEscapeKey(isActive: boolean, onEscape: () => void): void {\n useEffect(() => {\n if (!isActive) return;\n\n const handleEscape = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n onEscape();\n }\n };\n\n document.addEventListener('keydown', handleEscape);\n return () => document.removeEventListener('keydown', handleEscape);\n }, [isActive, onEscape]);\n}\n\n/**\n * Hook to detect clicks outside of specified element(s)\n * @param isActive - Whether to listen for clicks\n * @param refs - Array of refs to elements that should NOT trigger the callback\n * @param onClickOutside - Callback when clicking outside all refs\n */\nexport function useClickOutside(\n isActive: boolean,\n refs: RefObject<HTMLElement | null>[],\n onClickOutside: () => void\n): void {\n useEffect(() => {\n if (!isActive) return;\n\n const handleClickOutside = (e: MouseEvent) => {\n const clickedOutsideAll = refs.every(\n (ref) => ref.current && !ref.current.contains(e.target as Node)\n );\n if (clickedOutsideAll) {\n onClickOutside();\n }\n };\n\n document.addEventListener('mousedown', handleClickOutside);\n return () => document.removeEventListener('mousedown', handleClickOutside);\n }, [isActive, refs, onClickOutside]);\n}\n\n/**\n * Hook to prevent body scroll when modal is open\n * @param isActive - Whether to lock body scroll\n */\nexport function useLockBodyScroll(isActive: boolean): void {\n useEffect(() => {\n if (isActive) {\n document.body.style.overflow = 'hidden';\n } else {\n document.body.style.overflow = '';\n }\n return () => {\n document.body.style.overflow = '';\n };\n }, [isActive]);\n}\n"]}
|