@chrryai/chrry 1.1.78

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.
Files changed (116) hide show
  1. package/About.module.scss +112 -0
  2. package/Account.module.scss +159 -0
  3. package/AddToHomeScreen.module.scss +126 -0
  4. package/Affiliate.module.scss +245 -0
  5. package/AffiliateDashboard.module.scss +269 -0
  6. package/Agent.module.scss +197 -0
  7. package/AnimatedWrapper.module.scss +0 -0
  8. package/App.module.scss +283 -0
  9. package/Bookmark.module.scss +15 -0
  10. package/Calendar.module.scss +824 -0
  11. package/CharacterProfiles.module.scss +29 -0
  12. package/Chat.module.scss +932 -0
  13. package/Checkbox.module.scss +67 -0
  14. package/Chrry.module.scss +69 -0
  15. package/Collaborate.module.scss +22 -0
  16. package/CollaborationStatus.module.scss +17 -0
  17. package/CollaborationTooltip.module.scss +257 -0
  18. package/ColorScheme.module.scss +24 -0
  19. package/EditThread.module.scss +51 -0
  20. package/EmptyStateTips.module.scss +52 -0
  21. package/EnableNotifications.module.scss +20 -0
  22. package/EventModal.module.scss +277 -0
  23. package/Img.module.scss +48 -0
  24. package/Instructions.module.scss +333 -0
  25. package/LICENSE +664 -0
  26. package/LanguageSwitcher.module.scss +21 -0
  27. package/Loading.module.scss +32 -0
  28. package/MarkdownContent.module.scss +161 -0
  29. package/MemoryConsent.module.scss +41 -0
  30. package/Menu.module.scss +272 -0
  31. package/Message.module.scss +470 -0
  32. package/Messages.module.scss +86 -0
  33. package/Modal.module.scss +118 -0
  34. package/README.md +112 -0
  35. package/Search.module.scss +26 -0
  36. package/Select.module.scss +32 -0
  37. package/Share.module.scss +84 -0
  38. package/Sidebar.module.scss +69 -0
  39. package/SignIn.module.scss +129 -0
  40. package/Skeleton.module.scss +232 -0
  41. package/SplashScreen.module.scss +189 -0
  42. package/Star.module.scss +15 -0
  43. package/Store.module.scss +240 -0
  44. package/Subscribe.module.scss +219 -0
  45. package/Testimonials.module.scss +33 -0
  46. package/Thread.module.scss +111 -0
  47. package/Threads.module.scss +105 -0
  48. package/TypingIndicator.module.scss +93 -0
  49. package/Users.module.scss +98 -0
  50. package/Version.module.scss +22 -0
  51. package/Weather.module.scss +31 -0
  52. package/Why.module.scss +79 -0
  53. package/__tests__/README.md +126 -0
  54. package/__tests__/TestComponent.module.scss +236 -0
  55. package/animations.scss +163 -0
  56. package/breakpoints.scss +8 -0
  57. package/context/AppContext.module.scss +22 -0
  58. package/context/providers/README.md +329 -0
  59. package/dist/About.module-RPTFOKG6.scss +112 -0
  60. package/dist/Account.module-AA2NOD5S.scss +159 -0
  61. package/dist/AddToHomeScreen.module-P6HAQ4QD.scss +126 -0
  62. package/dist/Affiliate.module-YM7MG54E.scss +245 -0
  63. package/dist/AffiliateDashboard.module-SZQJJBME.scss +269 -0
  64. package/dist/Agent.module-66YIBDMM.scss +197 -0
  65. package/dist/App.module-TOWYJFPB.scss +283 -0
  66. package/dist/Bookmark.module-UVMQ4TED.scss +15 -0
  67. package/dist/Calendar.module-HHEIXJEA.scss +824 -0
  68. package/dist/CharacterProfiles.module-KABR34TV.scss +29 -0
  69. package/dist/Chat.module-Y4TGJLBQ.scss +932 -0
  70. package/dist/Checkbox.module-RNW2YOC5.scss +67 -0
  71. package/dist/Chrry.module-SLPTRY52.scss +69 -0
  72. package/dist/Collaborate.module-MLRE23FZ.scss +22 -0
  73. package/dist/CollaborationStatus.module-CRGOOW56.scss +17 -0
  74. package/dist/ColorScheme.module-N7SJ5N52.scss +24 -0
  75. package/dist/EditThread.module-L3HOEGS3.scss +51 -0
  76. package/dist/EmptyStateTips.module-JLNPQ4OO.scss +52 -0
  77. package/dist/EnableNotifications.module-C4MYQTUA.scss +20 -0
  78. package/dist/EventModal.module-K7VNKTCE.scss +277 -0
  79. package/dist/Instructions.module-JMFWEXAP.scss +333 -0
  80. package/dist/LanguageSwitcher.module-MT2SIZ4L.scss +21 -0
  81. package/dist/Loading.module-OU42QILE.scss +32 -0
  82. package/dist/MarkdownContent.module-64GHE3YP.scss +161 -0
  83. package/dist/MemoryConsent.module-FZL3REH4.scss +41 -0
  84. package/dist/Menu.module-RVXPXILR.scss +272 -0
  85. package/dist/Message.module-5UUYCVY2.scss +470 -0
  86. package/dist/Messages.module-GBPUAPAI.scss +86 -0
  87. package/dist/Modal.module-TOU4YLFQ.scss +118 -0
  88. package/dist/Search.module-GU3BRADG.scss +26 -0
  89. package/dist/Select.module-R7QM256C.scss +32 -0
  90. package/dist/Share.module-VQBCBSA5.scss +84 -0
  91. package/dist/Sidebar.module-AUSDVTCY.scss +69 -0
  92. package/dist/SignIn.module-53SOSG63.scss +129 -0
  93. package/dist/Skeleton.module-REMEBLDY.scss +232 -0
  94. package/dist/Store.module-NP6776GY.scss +240 -0
  95. package/dist/Subscribe.module-NVGQ57RA.scss +219 -0
  96. package/dist/Thread.module-QAJI6KOQ.scss +111 -0
  97. package/dist/Threads.module-J54DFQQZ.scss +105 -0
  98. package/dist/Users.module-ZRHCY63D.scss +98 -0
  99. package/dist/Version.module-MLMD7GW5.scss +22 -0
  100. package/dist/Weather.module-NT6XFKA7.scss +31 -0
  101. package/dist/Why.module-UVZJCJF7.scss +79 -0
  102. package/dist/index.d.mts +1408 -0
  103. package/dist/index.d.ts +1408 -0
  104. package/dist/index.js +44600 -0
  105. package/dist/index.js.map +1 -0
  106. package/dist/index.mjs +44575 -0
  107. package/dist/index.mjs.map +1 -0
  108. package/dist/react-tooltip.min-LLVNRY3Z.css +1 -0
  109. package/globals.css +91 -0
  110. package/globals.scss +585 -0
  111. package/icons/README.md +150 -0
  112. package/package.json +118 -0
  113. package/styles/view-transitions.css +207 -0
  114. package/toRem.scss +6 -0
  115. package/utils.module.scss +116 -0
  116. package/utils.scss +88 -0
@@ -0,0 +1,150 @@
1
+ # Platform-Aware Icon System
2
+
3
+ 1. Smart Meeting Scheduler 🗓️
4
+ Ask: "Schedule a team standup meeting tomorrow at 10 AM for 30 minutes with the title 'Daily Sync' and invite emma.brown@google.com"
5
+
6
+ Why it's impressive: Shows AI understanding multiple parameters (time, duration, title, attendees) and creating a complete calendar event in one command.
7
+
8
+ 2. Conflict Detector ⚠️
9
+ Ask: "Do I have any meetings between 2 PM and 5 PM today? If yes, can you reschedule my 3 PM call to tomorrow same time?"
10
+
11
+ Why it's impressive: Demonstrates AI reading calendar, detecting conflicts, and proactively suggesting/making changes.
12
+
13
+ 3. Weekly Overview 📊
14
+ Ask: "What's my schedule for this week? Highlight any back-to-back meetings and suggest breaks I should add."
15
+
16
+ Why it's impressive: Shows AI analyzing patterns, providing insights, and making smart recommendations for work-life balance.
17
+
18
+ 4. Natural Language Event Creation 🎯
19
+ Ask: "Block my calendar every Friday afternoon from 2-5 PM for the next month as 'Focus Time - No Meetings' and mark it as busy"
20
+
21
+ Why it's impressive: Demonstrates recurring event creation, natural language understanding, and calendar blocking strategy - perfect for productivity.
22
+
23
+ Pro tip for video: Start with #4 (most impressive), then show #1 (practical), then #3 (analytical), and end with #2 (problem-solving). This creates a compelling narrative from "wow" to "useful" to "intelligent."
24
+
25
+ This directory contains a platform-aware icon system that automatically uses the correct icon library based on the execution environment.
26
+
27
+ ## 🎯 Features
28
+
29
+ - **Automatic Platform Detection**: Uses `lucide-react` for web and `lucide-react-native` for native
30
+ - **Single Import Path**: Import from `chrry/icons` regardless of platform
31
+ - **Type-Safe**: Full TypeScript support with `IconProps`
32
+ - **Zero Configuration**: React Native Metro and Next.js handle it automatically
33
+
34
+ ## 📦 Usage
35
+
36
+ ```typescript
37
+ import { Star, Heart, Settings, Menu } from 'chrry/icons'
38
+
39
+ function MyComponent() {
40
+ return (
41
+ <div>
42
+ <Star size={24} color="gold" />
43
+ <Heart size={24} color="red" />
44
+ <Settings size={24} />
45
+ </div>
46
+ )
47
+ }
48
+ ```
49
+
50
+ ## 🔧 How It Works
51
+
52
+ The system uses platform-specific file extensions that bundlers automatically resolve:
53
+
54
+ ### File Structure
55
+
56
+ ```
57
+ icons/
58
+ ├── index.tsx # Default (web) - used by Next.js
59
+ ├── index.web.tsx # Web-specific exports (lucide-react)
60
+ └── index.native.tsx # Native-specific exports (lucide-react-native)
61
+ ```
62
+
63
+ ### Platform Resolution
64
+
65
+ - **Next.js**: Uses `index.tsx` → `index.web.tsx` → `lucide-react`
66
+ - **React Native Metro**: Uses `index.native.tsx` → `lucide-react-native`
67
+
68
+ This approach ensures:
69
+
70
+ - ✅ No React Native dependencies in web builds
71
+ - ✅ Proper native icons on ios/Android
72
+ - ✅ Single import path for all platforms
73
+
74
+ ## 🎨 Available Icons
75
+
76
+ All Lucide icons are available with consistent naming:
77
+
78
+ - `Star`, `Heart`, `Menu`, `Settings`
79
+ - `CircleX` (mapped from `X`)
80
+ - `CircleArrowLeft` (mapped from `ArrowLeft`)
81
+ - `ImageIcon` (mapped from `Image`)
82
+ - `VideoIcon` (mapped from `Video`)
83
+ - `FileIcon` (mapped from `File`)
84
+ - `TextIcon` (mapped from `Text`)
85
+ - And 100+ more...
86
+
87
+ ## 🔄 Migration from Tamagui
88
+
89
+ If you're migrating from `@tamagui/lucide-icons`:
90
+
91
+ ```typescript
92
+ // Before
93
+ import { Star, Heart } from "@tamagui/lucide-icons"
94
+
95
+ // After
96
+ import { Star, Heart } from "./icons"
97
+ ```
98
+
99
+ Run the replacement script:
100
+
101
+ ```bash
102
+ cd packages/ui
103
+ chmod +x replace-icons.sh
104
+ ./replace-icons.sh
105
+ ```
106
+
107
+ ## 🚀 Benefits
108
+
109
+ 1. **No React Native Dependencies on Web**: Avoids `react-native-svg` build errors in Next.js
110
+ 2. **True Cross-Platform**: Works seamlessly on web, ios, Android, and browser extensions
111
+ 3. **Automatic Resolution**: Bundlers handle platform detection at build time
112
+ 4. **Type Safety**: Full TypeScript support with proper types for each platform
113
+
114
+ ## 📝 Adding New Icons
115
+
116
+ All Lucide icons are automatically available via `export *`. Just import and use:
117
+
118
+ ```typescript
119
+ import { NewIcon } from 'chrry/icons'
120
+
121
+ // Works immediately if the icon exists in lucide-react/lucide-react-native
122
+ <NewIcon size={24} />
123
+ ```
124
+
125
+ No manual exports needed!
126
+
127
+ ## 🐛 Troubleshooting
128
+
129
+ ### TypeScript errors about missing icons
130
+
131
+ - Make sure both `lucide-react` and `lucide-react-native` are installed
132
+ - Run `npm install` in the `packages/ui` directory
133
+
134
+ ### Icons not rendering on web
135
+
136
+ - Verify the icon exists in `lucide-react`
137
+ - Check the import path: `from 'chrry/icons'`
138
+ - Clear Next.js cache: `rm -rf .next && npm run dev`
139
+
140
+ ### Icons not rendering on native
141
+
142
+ - Verify the icon exists in `lucide-react-native`
143
+ - Check that Metro bundler is resolving `index.native.tsx`
144
+ - Clear Metro cache: `npm start -- --reset-cache`
145
+
146
+ ### Build errors about react-native-svg
147
+
148
+ - This should not happen with the new structure
149
+ - Verify `index.tsx` exports from `./index.web` (not direct lucide-react-native import)
150
+ - Ensure Next.js is not trying to bundle native files
package/package.json ADDED
@@ -0,0 +1,118 @@
1
+ {
2
+ "name": "@chrryai/chrry",
3
+ "version": "1.1.78",
4
+ "description": "A modern, cross-platform UI library for React, React Native, and Next.js",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.mjs",
12
+ "require": "./dist/index.js"
13
+ },
14
+ "./*": "./*"
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "**/*.css",
19
+ "**/*.scss",
20
+ "README.md"
21
+ ],
22
+ "private": false,
23
+ "publishConfig": {
24
+ "access": "public"
25
+ },
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "https://github.com/chrryai/chrry.git"
29
+ },
30
+ "keywords": [
31
+ "react",
32
+ "react-native",
33
+ "nextjs",
34
+ "ui",
35
+ "components",
36
+ "cross-platform",
37
+ "typescript"
38
+ ],
39
+ "author": "Iliyan Velinov",
40
+ "license": "AGPL-3.0",
41
+ "bugs": {
42
+ "url": "https://github.com/chrryai/chrry/issues"
43
+ },
44
+ "homepage": "https://chrry.dev",
45
+ "scripts": {
46
+ "build": "tsup",
47
+ "dev": "tsup --watch",
48
+ "lint": "eslint . --max-warnings 1500",
49
+ "generate:component": "turbo gen react-component",
50
+ "check-types": "tsc --noEmit",
51
+ "prepublishOnly": "npm run build"
52
+ },
53
+ "devDependencies": {
54
+ "@repo/eslint-config": "*",
55
+ "@repo/typescript-config": "*",
56
+ "@types/node": "^22.15.3",
57
+ "@types/react": "19.1.12",
58
+ "eslint": "^9.34.0",
59
+ "tsup": "^8.0.0",
60
+ "typescript": "5.8.2"
61
+ },
62
+ "peerDependencies": {
63
+ "react": "^18.0.0 || ^19.0.0",
64
+ "react-dom": "^18.0.0 || ^19.0.0"
65
+ },
66
+ "dependencies": {
67
+ "@hookform/resolvers": "^5.2.2",
68
+ "@lobehub/icons": "^2.23.0",
69
+ "@react-native-community/netinfo": "^11.4.1",
70
+ "@react-spring/native": "^10.0.3",
71
+ "@react-spring/web": "^10.0.3",
72
+ "@stripe/stripe-js": "^7.7.0",
73
+ "@tanstack/react-query": "^5.83.0",
74
+ "@types/chrome": "^0.1.1",
75
+ "@types/nprogress": "^0.2.3",
76
+ "@types/react-dom": "19.1.1",
77
+ "@types/react-syntax-highlighter": "^15.5.13",
78
+ "@types/sanitize-html": "^2.16.0",
79
+ "@uidotdev/usehooks": "^2.4.1",
80
+ "ai": "^5.0.0",
81
+ "antd": "^5.26.7",
82
+ "clsx": "^2.1.1",
83
+ "date-fns": "^4.1.0",
84
+ "emoji-picker-react": "^4.14.0",
85
+ "i18next": "^25.3.4",
86
+ "javascript-time-ago": "^2.5.11",
87
+ "lucide-react": "^0.534.0",
88
+ "lucide-react-native": "^0.462.0",
89
+ "markdown-to-jsx": "^7.7.13",
90
+ "next-auth": "^4.24.11",
91
+ "next-themes": "^0.4.6",
92
+ "nextjs-toploader": "^3.8.16",
93
+ "nprogress": "^0.2.0",
94
+ "react": "^19.1.1",
95
+ "react-audio-play": "^1.0.4",
96
+ "react-big-calendar": "^1.19.4",
97
+ "react-dnd": "^16.0.1",
98
+ "react-dnd-html5-backend": "^16.0.1",
99
+ "react-dom": "^19.1.1",
100
+ "react-hook-form": "^7.63.0",
101
+ "react-hot-toast": "^2.5.2",
102
+ "react-i18next": "^15.6.1",
103
+ "react-icons": "^5.5.0",
104
+ "react-intersection-observer": "^9.16.0",
105
+ "react-markdown": "^10.1.0",
106
+ "react-native-reanimated": "^4.1.3",
107
+ "react-select": "^5.10.2",
108
+ "react-syntax-highlighter": "^15.6.1",
109
+ "react-use-cookie": "^1.6.1",
110
+ "remark-gfm": "^4.0.1",
111
+ "sanitize-html": "^2.17.0",
112
+ "sass": "^1.90.0",
113
+ "superjson": "^2.2.2",
114
+ "use-debounce": "^10.0.5",
115
+ "usehooks-ts": "^3.1.1",
116
+ "zod": "^4.1.11"
117
+ }
118
+ }
@@ -0,0 +1,207 @@
1
+ /* View Transitions API - Optimized for in-page transitions */
2
+
3
+ /* ============================================
4
+ DEFAULT: Smooth page transitions
5
+ Desktop: Slide up/down
6
+ Mobile: Slide left/right
7
+ ============================================ */
8
+
9
+ /* Desktop: Smooth slide-up animation for page changes */
10
+ ::view-transition-old(root),
11
+ ::view-transition-new(root) {
12
+ animation-duration: 0.3s;
13
+ animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
14
+ }
15
+
16
+ ::view-transition-old(root) {
17
+ animation-name: slide-down;
18
+ }
19
+
20
+ ::view-transition-new(root) {
21
+ animation-name: slide-up;
22
+ }
23
+
24
+ /* Mobile: Slide aside (left/right) for more natural mobile feel */
25
+ @media (max-width: 768px) {
26
+ ::view-transition-old(root) {
27
+ animation-name: slide-to-left;
28
+ }
29
+
30
+ ::view-transition-new(root) {
31
+ animation-name: slide-from-right;
32
+ }
33
+ }
34
+
35
+ /* ============================================
36
+ KEYFRAMES - Reusable animations
37
+ ============================================ */
38
+
39
+ @keyframes fade-out {
40
+ to {
41
+ opacity: 0;
42
+ }
43
+ }
44
+
45
+ @keyframes fade-in {
46
+ from {
47
+ opacity: 0;
48
+ }
49
+ }
50
+
51
+ @keyframes scale-up {
52
+ from {
53
+ transform: scale(0.9);
54
+ opacity: 0;
55
+ }
56
+ }
57
+
58
+ @keyframes scale-down {
59
+ to {
60
+ transform: scale(0.9);
61
+ opacity: 0;
62
+ }
63
+ }
64
+
65
+ @keyframes slide-up {
66
+ from {
67
+ transform: translateY(10px);
68
+ opacity: 0;
69
+ }
70
+ }
71
+
72
+ @keyframes slide-down {
73
+ to {
74
+ transform: translateY(10px);
75
+ opacity: 0;
76
+ }
77
+ }
78
+
79
+ @keyframes slide-to-left {
80
+ to {
81
+ transform: translateX(-100%);
82
+ opacity: 0;
83
+ }
84
+ }
85
+
86
+ @keyframes slide-from-right {
87
+ from {
88
+ transform: translateX(100%);
89
+ opacity: 0;
90
+ }
91
+ }
92
+
93
+ /* ============================================
94
+ IN-PAGE TRANSITIONS (Always enabled)
95
+ ============================================ */
96
+
97
+ @media (prefers-reduced-motion: no-preference) {
98
+ /* App image - smooth morph transition */
99
+ ::view-transition-old(app-image),
100
+ ::view-transition-new(app-image) {
101
+ animation-duration: 0.4s;
102
+ animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
103
+ overflow: clip;
104
+ mix-blend-mode: normal;
105
+ }
106
+
107
+ ::view-transition-old(app-image) {
108
+ animation-name: scale-down;
109
+ }
110
+
111
+ ::view-transition-new(app-image) {
112
+ animation-name: scale-up;
113
+ }
114
+
115
+ /* App title - smooth slide and fade */
116
+ ::view-transition-old(app-title),
117
+ ::view-transition-new(app-title) {
118
+ animation-duration: 0.35s;
119
+ animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
120
+ }
121
+
122
+ ::view-transition-old(app-title) {
123
+ animation-name: slide-down;
124
+ }
125
+
126
+ ::view-transition-new(app-title) {
127
+ animation-name: slide-up;
128
+ }
129
+
130
+ /* Image enlargement - smooth scale transition */
131
+ ::view-transition-old(image-enlarge),
132
+ ::view-transition-new(image-enlarge) {
133
+ animation-duration: 0.3s;
134
+ animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
135
+ overflow: clip;
136
+ }
137
+
138
+ ::view-transition-old(image-enlarge) {
139
+ animation-name: scale-down;
140
+ }
141
+
142
+ ::view-transition-new(image-enlarge) {
143
+ animation-name: scale-up;
144
+ }
145
+
146
+ /* Modal/Dialog transitions */
147
+ ::view-transition-old(modal),
148
+ ::view-transition-new(modal) {
149
+ animation-duration: 0.25s;
150
+ animation-timing-function: ease-in-out;
151
+ }
152
+
153
+ ::view-transition-old(modal) {
154
+ animation-name: fade-out;
155
+ }
156
+
157
+ ::view-transition-new(modal) {
158
+ animation-name: scale-up;
159
+ }
160
+
161
+ /* Content sections - subtle fade */
162
+ ::view-transition-old(content-section),
163
+ ::view-transition-new(content-section) {
164
+ animation-duration: 0.2s;
165
+ animation-timing-function: ease-in-out;
166
+ }
167
+
168
+ ::view-transition-old(content-section) {
169
+ animation-name: fade-out;
170
+ }
171
+
172
+ ::view-transition-new(content-section) {
173
+ animation-name: fade-in;
174
+ }
175
+ }
176
+
177
+ /* ============================================
178
+ OPTIONAL: Enable full-page transitions
179
+ Add .page-transitions class to <html> or <body>
180
+ ============================================ */
181
+
182
+ .page-transitions {
183
+ @media (prefers-reduced-motion: no-preference) {
184
+ ::view-transition-old(root),
185
+ ::view-transition-new(root) {
186
+ animation-duration: 0.2s;
187
+ animation-timing-function: ease-in-out;
188
+ }
189
+
190
+ ::view-transition-old(root) {
191
+ animation-name: fade-out;
192
+ }
193
+
194
+ ::view-transition-new(root) {
195
+ animation-name: fade-in;
196
+ }
197
+
198
+ /* Main content with page transitions enabled */
199
+ ::view-transition-old(main-content) {
200
+ animation: fade-out 0.15s ease-out;
201
+ }
202
+
203
+ ::view-transition-new(main-content) {
204
+ animation: fade-in 0.2s ease-in;
205
+ }
206
+ }
207
+ }
package/toRem.scss ADDED
@@ -0,0 +1,6 @@
1
+ @use "sass:math";
2
+
3
+ @function toRem($value) {
4
+ $remValue: math.div($value, 16) + rem;
5
+ @return $remValue;
6
+ }
@@ -0,0 +1,116 @@
1
+ @use "./toRem.scss";
2
+
3
+ // Utility classes for cross-platform styling
4
+ .button {
5
+ padding: toRem.toRem(8) toRem.toRem(12);
6
+ background-color: var(--link-color);
7
+ color: #fff;
8
+ display: inline-flex;
9
+ align-items: center;
10
+ gap: toRem.toRem(5);
11
+ border: none;
12
+ border-radius: var(--radius);
13
+ cursor: pointer;
14
+ box-shadow: var(--shadow);
15
+ font-size: toRem.toRem(14);
16
+ line-height: 1.15;
17
+
18
+ &:hover {
19
+ background-color: var(--accent-5);
20
+ color: #fff;
21
+ }
22
+
23
+ &:active {
24
+ transform: translateY(toRem.toRem(1.2));
25
+ }
26
+
27
+ &:disabled {
28
+ cursor: default;
29
+ background-color: var(--background);
30
+ color: var(--foreground);
31
+ }
32
+ }
33
+
34
+ .link {
35
+ text-decoration: none;
36
+ color: var(--accent-6);
37
+ cursor: pointer;
38
+ background-color: transparent;
39
+ box-shadow: none;
40
+ padding: 0;
41
+ margin: 0;
42
+ display: inline-flex;
43
+ align-items: center;
44
+ gap: toRem.toRem(5);
45
+
46
+ &:hover {
47
+ text-decoration: none;
48
+ color: var(--accent-5);
49
+ background-color: transparent;
50
+ }
51
+
52
+ &:active {
53
+ transform: translateY(toRem.toRem(1.2));
54
+ }
55
+ }
56
+
57
+ .small {
58
+ padding: toRem.toRem(5.6) toRem.toRem(8.8);
59
+ font-size: toRem.toRem(14);
60
+ }
61
+
62
+ .xSmall {
63
+ padding: toRem.toRem(4) toRem.toRem(7.2);
64
+ font-size: toRem.toRem(12);
65
+ }
66
+
67
+ .large {
68
+ padding: toRem.toRem(8) toRem.toRem(20);
69
+ font-size: toRem.toRem(16);
70
+ }
71
+
72
+ .transparent {
73
+ background-color: var(--background);
74
+ color: var(--foreground);
75
+ border: toRem.toRem(1) solid var(--shade-2);
76
+ box-shadow: none;
77
+
78
+ &:hover {
79
+ background-color: var(--shade-1);
80
+ color: var(--foreground);
81
+ }
82
+ }
83
+
84
+ .inverted {
85
+ background-color: var(--foreground);
86
+ color: var(--background);
87
+ border: toRem.toRem(1) solid var(--shade-2);
88
+ box-shadow: none;
89
+
90
+ &:hover {
91
+ background-color: var(--shade-7);
92
+ color: var(--background);
93
+ }
94
+ }
95
+
96
+ .row {
97
+ display: flex;
98
+ flex-direction: row;
99
+ align-items: center;
100
+ gap: toRem.toRem(5);
101
+ flex-wrap: wrap;
102
+ }
103
+
104
+ .column {
105
+ display: flex;
106
+ flex-direction: column;
107
+ gap: toRem.toRem(5);
108
+ }
109
+
110
+ .left {
111
+ margin-right: auto;
112
+ }
113
+
114
+ .right {
115
+ margin-left: auto;
116
+ }
package/utils.scss ADDED
@@ -0,0 +1,88 @@
1
+ @use "./toRem.scss";
2
+
3
+ @function toRem($value) {
4
+ $remValue: math.div($value, 16) + rem;
5
+ @return $remValue;
6
+ }
7
+
8
+ @mixin respect-reduced-motion {
9
+ @media (prefers-reduced-motion: reduce) {
10
+ animation: none !important;
11
+ transition: none !important;
12
+ }
13
+ }
14
+
15
+ @keyframes slideLeft {
16
+ from {
17
+ opacity: 0;
18
+ transform: translateX(toRem.toRem(-20)); /* Start slightly left */
19
+ }
20
+ to {
21
+ opacity: 1;
22
+ transform: translateX(0); /* End at original position */
23
+ }
24
+ }
25
+
26
+ @keyframes slideRight {
27
+ from {
28
+ opacity: 0;
29
+ transform: translateX(toRem.toRem(20)); /* Start slightly right */
30
+ }
31
+ to {
32
+ opacity: 1;
33
+ transform: translateX(0); /* End at original position */
34
+ }
35
+ }
36
+ @keyframes slideUp {
37
+ from {
38
+ opacity: 0;
39
+ transform: translateY(toRem.toRem(20)); /* Start slightly below */
40
+ }
41
+ to {
42
+ opacity: 1;
43
+ transform: translateY(0); /* End at original position */
44
+ }
45
+ }
46
+
47
+ @keyframes blink {
48
+ 0%,
49
+ 100% {
50
+ opacity: 1;
51
+ }
52
+ 50% {
53
+ opacity: 0;
54
+ }
55
+ }
56
+
57
+ @keyframes pulse {
58
+ 0% {
59
+ transform: scale(0.9);
60
+ opacity: 0.7;
61
+ }
62
+ 50% {
63
+ transform: scale(1.1);
64
+ opacity: 1;
65
+ }
66
+ 100% {
67
+ transform: scale(0.9);
68
+ opacity: 0.7;
69
+ }
70
+ }
71
+ @keyframes float {
72
+ 0%,
73
+ 100% {
74
+ transform: translateY(0);
75
+ }
76
+ 50% {
77
+ transform: translateY(-5px);
78
+ }
79
+ }
80
+ @keyframes wiggle {
81
+ 0%,
82
+ 100% {
83
+ transform: rotate(-5deg);
84
+ }
85
+ 50% {
86
+ transform: rotate(5deg);
87
+ }
88
+ }