ChunDe Claude commited on
Commit
d06267b
·
1 Parent(s): 72660a6

feat: Add responsive UI scaling and fix Bison font loading

Browse files

- Implement 10% UI scaling reduction below 1024px breakpoint
- Scale all UI components: Sidebar, ExportButton, BgColorSelector, CanvasSizeSelector, TextToolbar
- Fix Bison font loading by removing parentheses from filename
- Add font preload for better loading performance
- Clean up debug console.logs from Canvas components
- Add resize listeners for proper responsive behavior

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>

.claude/settings.local.json CHANGED
@@ -37,7 +37,8 @@
37
  "Bash(git config:*)",
38
  "Bash(vite build:*)",
39
  "Bash(npx vite build)",
40
- "Bash(git fetch:*)"
 
41
  ],
42
  "deny": [],
43
  "ask": []
 
37
  "Bash(git config:*)",
38
  "Bash(vite build:*)",
39
  "Bash(npx vite build)",
40
+ "Bash(git fetch:*)",
41
+ "Bash(move:*)"
42
  ],
43
  "deny": [],
44
  "ask": []
dist/index.html CHANGED
@@ -11,13 +11,16 @@
11
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
12
  <link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,400;0,700;1,400;1,700&family=Inter:wght@400;700&family=Source+Sans+3:ital,wght@0,400;0,700;0,900;1,400;1,700;1,900&display=swap" rel="stylesheet">
13
 
 
 
 
14
  <!-- Preconnect to Hugging Face CDN for faster Huggy image loading -->
15
  <link rel="preconnect" href="https://datasets-server.huggingface.co" crossorigin>
16
  <link rel="dns-prefetch" href="https://datasets-server.huggingface.co">
17
- <script type="module" crossorigin src="/assets/index-B75Va-HN.js"></script>
18
  <link rel="modulepreload" crossorigin href="/assets/react-vendor-DzFEYc3-.js">
19
  <link rel="modulepreload" crossorigin href="/assets/konva-vendor-CFBUcegD.js">
20
- <link rel="stylesheet" crossorigin href="/assets/index-C5nIs3Dc.css">
21
  </head>
22
  <body>
23
  <div id="root"></div>
 
11
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
12
  <link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,400;0,700;1,400;1,700&family=Inter:wght@400;700&family=Source+Sans+3:ital,wght@0,400;0,700;0,900;1,400;1,700;1,900&display=swap" rel="stylesheet">
13
 
14
+ <!-- Preload Bison font -->
15
+ <link rel="preload" href="/fonts/Bison-Bold.ttf" as="font" type="font/ttf" crossorigin>
16
+
17
  <!-- Preconnect to Hugging Face CDN for faster Huggy image loading -->
18
  <link rel="preconnect" href="https://datasets-server.huggingface.co" crossorigin>
19
  <link rel="dns-prefetch" href="https://datasets-server.huggingface.co">
20
+ <script type="module" crossorigin src="/assets/index-DdtTDuVR.js"></script>
21
  <link rel="modulepreload" crossorigin href="/assets/react-vendor-DzFEYc3-.js">
22
  <link rel="modulepreload" crossorigin href="/assets/konva-vendor-CFBUcegD.js">
23
+ <link rel="stylesheet" crossorigin href="/assets/index-D-X_GJYx.css">
24
  </head>
25
  <body>
26
  <div id="root"></div>
index.html CHANGED
@@ -11,6 +11,9 @@
11
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
12
  <link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,400;0,700;1,400;1,700&family=Inter:wght@400;700&family=Source+Sans+3:ital,wght@0,400;0,700;0,900;1,400;1,700;1,900&display=swap" rel="stylesheet">
13
 
 
 
 
14
  <!-- Preconnect to Hugging Face CDN for faster Huggy image loading -->
15
  <link rel="preconnect" href="https://datasets-server.huggingface.co" crossorigin>
16
  <link rel="dns-prefetch" href="https://datasets-server.huggingface.co">
 
11
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
12
  <link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,400;0,700;1,400;1,700&family=Inter:wght@400;700&family=Source+Sans+3:ital,wght@0,400;0,700;0,900;1,400;1,700;1,900&display=swap" rel="stylesheet">
13
 
14
+ <!-- Preload Bison font -->
15
+ <link rel="preload" href="/fonts/Bison-Bold.ttf" as="font" type="font/ttf" crossorigin>
16
+
17
  <!-- Preconnect to Hugging Face CDN for faster Huggy image loading -->
18
  <link rel="preconnect" href="https://datasets-server.huggingface.co" crossorigin>
19
  <link rel="dns-prefetch" href="https://datasets-server.huggingface.co">
package-lock.json CHANGED
@@ -16,6 +16,7 @@
16
  "use-image": "^1.1.4"
17
  },
18
  "devDependencies": {
 
19
  "@types/react": "^18.3.3",
20
  "@types/react-dom": "^18.3.0",
21
  "@vitejs/plugin-react": "^4.3.1",
@@ -1196,6 +1197,16 @@
1196
  "dev": true,
1197
  "license": "MIT"
1198
  },
 
 
 
 
 
 
 
 
 
 
1199
  "node_modules/@types/prop-types": {
1200
  "version": "15.7.15",
1201
  "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz",
@@ -2969,6 +2980,13 @@
2969
  "node": ">=14.17"
2970
  }
2971
  },
 
 
 
 
 
 
 
2972
  "node_modules/update-browserslist-db": {
2973
  "version": "1.1.4",
2974
  "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz",
 
16
  "use-image": "^1.1.4"
17
  },
18
  "devDependencies": {
19
+ "@types/node": "^24.10.1",
20
  "@types/react": "^18.3.3",
21
  "@types/react-dom": "^18.3.0",
22
  "@vitejs/plugin-react": "^4.3.1",
 
1197
  "dev": true,
1198
  "license": "MIT"
1199
  },
1200
+ "node_modules/@types/node": {
1201
+ "version": "24.10.1",
1202
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz",
1203
+ "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==",
1204
+ "dev": true,
1205
+ "license": "MIT",
1206
+ "dependencies": {
1207
+ "undici-types": "~7.16.0"
1208
+ }
1209
+ },
1210
  "node_modules/@types/prop-types": {
1211
  "version": "15.7.15",
1212
  "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz",
 
2980
  "node": ">=14.17"
2981
  }
2982
  },
2983
+ "node_modules/undici-types": {
2984
+ "version": "7.16.0",
2985
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
2986
+ "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
2987
+ "dev": true,
2988
+ "license": "MIT"
2989
+ },
2990
  "node_modules/update-browserslist-db": {
2991
  "version": "1.1.4",
2992
  "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz",
package.json CHANGED
@@ -18,6 +18,7 @@
18
  "use-image": "^1.1.4"
19
  },
20
  "devDependencies": {
 
21
  "@types/react": "^18.3.3",
22
  "@types/react-dom": "^18.3.0",
23
  "@vitejs/plugin-react": "^4.3.1",
 
18
  "use-image": "^1.1.4"
19
  },
20
  "devDependencies": {
21
+ "@types/node": "^24.10.1",
22
  "@types/react": "^18.3.3",
23
  "@types/react-dom": "^18.3.0",
24
  "@vitejs/plugin-react": "^4.3.1",
public/fonts/Bison-Bold(PersonalUse).ttf DELETED
Binary file (28 kB)
 
dist/fonts/Bison-Bold(PersonalUse).ttf → public/fonts/Bison-Bold.ttf RENAMED
File without changes
src/components/Canvas/Canvas.tsx CHANGED
@@ -178,10 +178,7 @@ export default function Canvas({
178
  .map(id => shapeRefs.current.get(id))
179
  .filter((node): node is Konva.Node => node !== undefined);
180
 
181
- console.log(`Attempt ${attempt}: Attaching transformer to:`, selectedIds, 'Found nodes:', nodes.length);
182
-
183
  if (nodes.length > 0) {
184
- console.log('SUCCESS: Attaching transformer to', nodes.length, 'nodes');
185
  transformerRef.current!.nodes(nodes);
186
  transformerRef.current!.show();
187
  transformerRef.current!.forceUpdate();
@@ -189,13 +186,10 @@ export default function Canvas({
189
  if (layer) {
190
  layer.batchDraw();
191
  }
192
- console.log('Transformer attached and layer redrawn');
193
  } else if (attempt < 10) {
194
  // Retry up to 10 times with increasing delays (up to 500ms)
195
  const delay = Math.min(50 * (attempt + 1), 100);
196
  setTimeout(() => attemptAttachment(attempt + 1), delay);
197
- } else {
198
- console.error('Failed to attach transformer after 10 attempts. Selected IDs:', selectedIds);
199
  }
200
  } else {
201
  transformerRef.current!.nodes([]);
 
178
  .map(id => shapeRefs.current.get(id))
179
  .filter((node): node is Konva.Node => node !== undefined);
180
 
 
 
181
  if (nodes.length > 0) {
 
182
  transformerRef.current!.nodes(nodes);
183
  transformerRef.current!.show();
184
  transformerRef.current!.forceUpdate();
 
186
  if (layer) {
187
  layer.batchDraw();
188
  }
 
189
  } else if (attempt < 10) {
190
  // Retry up to 10 times with increasing delays (up to 500ms)
191
  const delay = Math.min(50 * (attempt + 1), 100);
192
  setTimeout(() => attemptAttachment(attempt + 1), delay);
 
 
193
  }
194
  } else {
195
  transformerRef.current!.nodes([]);
src/components/Canvas/CanvasObject.tsx CHANGED
@@ -164,7 +164,6 @@ function ImageRenderer({
164
  id={object.id}
165
  ref={(node) => {
166
  nodeRef.current = node;
167
- console.log('ImageRenderer ref callback:', object.id, 'Node:', node);
168
  if (typeof shapeRef === 'function') {
169
  shapeRef(node);
170
  } else if (shapeRef) {
 
164
  id={object.id}
165
  ref={(node) => {
166
  nodeRef.current = node;
 
167
  if (typeof shapeRef === 'function') {
168
  shapeRef(node);
169
  } else if (shapeRef) {
src/components/CanvasHeader/BgColorSelector.tsx CHANGED
@@ -13,44 +13,21 @@ export default function BgColorSelector({ bgColor, onChange }: BgColorSelectorPr
13
  const [hoveredColor, setHoveredColor] = useState<CanvasBgColor | null>(null);
14
 
15
  return (
16
- <div style={{ display: 'flex', alignItems: 'center', gap: '10px' }}>
17
- <span style={{
18
- color: '#999999',
19
- fontSize: '16px',
20
- fontWeight: 'normal',
21
- fontFamily: 'Inter, sans-serif'
22
- }}>
23
  Background color:
24
  </span>
25
 
26
  {/* Outer pill container */}
27
- <div style={{
28
- display: 'flex',
29
- alignItems: 'center',
30
- gap: '5px',
31
- height: '40px',
32
- padding: '4px',
33
- background: '#EDF0F2',
34
- border: '1px solid #F8F9FA',
35
- borderRadius: '99px'
36
- }}>
37
  {/* Serious Light option */}
38
  <button
39
  onClick={() => onChange('seriousLight')}
40
  onMouseEnter={() => setHoveredColor('seriousLight')}
41
  onMouseLeave={() => setHoveredColor(null)}
 
42
  style={{
43
- display: 'flex',
44
- alignItems: 'center',
45
- justifyContent: 'center',
46
- width: '38px',
47
- height: '32px',
48
- padding: '10px',
49
- background: bgColor === 'seriousLight' ? '#DEE2E7' : (hoveredColor === 'seriousLight' ? '#f0f2f4' : 'transparent'),
50
- border: 'none',
51
- borderRadius: '99px',
52
- cursor: 'pointer',
53
- transition: 'background 0.15s ease-in-out'
54
  }}
55
  title="Serious Light background"
56
  >
@@ -62,18 +39,9 @@ export default function BgColorSelector({ bgColor, onChange }: BgColorSelectorPr
62
  onClick={() => onChange('light')}
63
  onMouseEnter={() => setHoveredColor('light')}
64
  onMouseLeave={() => setHoveredColor(null)}
 
65
  style={{
66
- display: 'flex',
67
- alignItems: 'center',
68
- justifyContent: 'center',
69
- width: '38px',
70
- height: '32px',
71
- padding: '10px',
72
- background: bgColor === 'light' ? '#DEE2E7' : (hoveredColor === 'light' ? '#f0f2f4' : 'transparent'),
73
- border: 'none',
74
- borderRadius: '99px',
75
- cursor: 'pointer',
76
- transition: 'background 0.15s ease-in-out'
77
  }}
78
  title="Light background with gradients"
79
  >
@@ -85,18 +53,9 @@ export default function BgColorSelector({ bgColor, onChange }: BgColorSelectorPr
85
  onClick={() => onChange('dark')}
86
  onMouseEnter={() => setHoveredColor('dark')}
87
  onMouseLeave={() => setHoveredColor(null)}
 
88
  style={{
89
- display: 'flex',
90
- alignItems: 'center',
91
- justifyContent: 'center',
92
- width: '38px',
93
- height: '32px',
94
- padding: '10px',
95
- background: bgColor === 'dark' ? '#DEE2E7' : (hoveredColor === 'dark' ? '#f0f2f4' : 'transparent'),
96
- border: 'none',
97
- borderRadius: '99px',
98
- cursor: 'pointer',
99
- transition: 'background 0.15s ease-in-out'
100
  }}
101
  title="Dark background"
102
  >
 
13
  const [hoveredColor, setHoveredColor] = useState<CanvasBgColor | null>(null);
14
 
15
  return (
16
+ <div className="flex items-center gap-[9px] lg:gap-[10px]">
17
+ <span className="text-[#999999] text-[14px] lg:text-[16px] font-normal" style={{ fontFamily: 'Inter, sans-serif' }}>
 
 
 
 
 
18
  Background color:
19
  </span>
20
 
21
  {/* Outer pill container */}
22
+ <div className="flex items-center gap-[4.5px] lg:gap-[5px] h-[36px] lg:h-[40px] p-[3.6px] lg:p-[4px] bg-[#EDF0F2] border border-[#F8F9FA] rounded-[99px]">
 
 
 
 
 
 
 
 
 
23
  {/* Serious Light option */}
24
  <button
25
  onClick={() => onChange('seriousLight')}
26
  onMouseEnter={() => setHoveredColor('seriousLight')}
27
  onMouseLeave={() => setHoveredColor(null)}
28
+ className="flex items-center justify-center w-[34px] lg:w-[38px] h-[29px] lg:h-[32px] p-[9px] lg:p-[10px] border-none rounded-[99px] cursor-pointer transition-[background] duration-150 ease-in-out"
29
  style={{
30
+ background: bgColor === 'seriousLight' ? '#DEE2E7' : (hoveredColor === 'seriousLight' ? '#f0f2f4' : 'transparent')
 
 
 
 
 
 
 
 
 
 
31
  }}
32
  title="Serious Light background"
33
  >
 
39
  onClick={() => onChange('light')}
40
  onMouseEnter={() => setHoveredColor('light')}
41
  onMouseLeave={() => setHoveredColor(null)}
42
+ className="flex items-center justify-center w-[34px] lg:w-[38px] h-[29px] lg:h-[32px] p-[9px] lg:p-[10px] border-none rounded-[99px] cursor-pointer transition-[background] duration-150 ease-in-out"
43
  style={{
44
+ background: bgColor === 'light' ? '#DEE2E7' : (hoveredColor === 'light' ? '#f0f2f4' : 'transparent')
 
 
 
 
 
 
 
 
 
 
45
  }}
46
  title="Light background with gradients"
47
  >
 
53
  onClick={() => onChange('dark')}
54
  onMouseEnter={() => setHoveredColor('dark')}
55
  onMouseLeave={() => setHoveredColor(null)}
56
+ className="flex items-center justify-center w-[34px] lg:w-[38px] h-[29px] lg:h-[32px] p-[9px] lg:p-[10px] border-none rounded-[99px] cursor-pointer transition-[background] duration-150 ease-in-out"
57
  style={{
58
+ background: bgColor === 'dark' ? '#DEE2E7' : (hoveredColor === 'dark' ? '#f0f2f4' : 'transparent')
 
 
 
 
 
 
 
 
 
 
59
  }}
60
  title="Dark background"
61
  >
src/components/CanvasHeader/CanvasSizeSelector.tsx CHANGED
@@ -1,4 +1,4 @@
1
- import { useState } from 'react';
2
  import { CanvasSize } from '../../types/canvas.types';
3
  import IconXSize from '../Icons/IconXSize';
4
  import IconLinkedInSize from '../Icons/IconLinkedInSize';
@@ -17,6 +17,16 @@ const sizeLabels: Record<CanvasSize, string> = {
17
 
18
  export default function CanvasSizeSelector({ canvasSize, onChange }: CanvasSizeSelectorProps) {
19
  const [hoveredSize, setHoveredSize] = useState<CanvasSize | null>(null);
 
 
 
 
 
 
 
 
 
 
20
 
21
  const renderSizeButton = (
22
  size: CanvasSize,
@@ -31,37 +41,29 @@ export default function CanvasSizeSelector({ canvasSize, onChange }: CanvasSizeS
31
  onClick={() => onChange(size)}
32
  onMouseEnter={() => setHoveredSize(size)}
33
  onMouseLeave={() => setHoveredSize(null)}
 
34
  style={{
35
- display: 'flex',
36
- alignItems: 'center',
37
  justifyContent: isSelected ? 'flex-start' : 'center',
38
- gap: '5px',
39
- height: '32px',
40
- minWidth: '38px',
41
- paddingLeft: isSelected ? '10px' : '9px',
42
- paddingRight: isSelected ? '10px' : '9px',
43
- background: isSelected ? '#DEE2E7' : (isHovered ? '#f0f2f4' : 'transparent'),
44
- border: 'none',
45
- borderRadius: '99px',
46
- cursor: 'pointer',
47
- transition: 'background 0.15s ease-in-out, padding 0.15s ease-in-out, min-width 0.15s ease-in-out',
48
- overflow: 'hidden'
49
  }}
50
  title={title}
51
  >
52
  <Icon selected={isSelected} />
53
  <span
 
54
  style={{
55
  color: '#32343d',
56
- fontSize: '16px',
57
  fontWeight: 'normal',
58
  fontFamily: 'Inter, sans-serif',
59
- whiteSpace: 'nowrap',
60
  opacity: isSelected ? 0.6 : 0,
61
  transform: isSelected ? 'translateX(0)' : 'translateX(-10px)',
62
- transition: 'opacity 0.15s ease-in-out, transform 0.15s ease-in-out',
63
- width: isSelected ? 'auto' : '0',
64
- overflow: 'hidden'
65
  }}
66
  >
67
  {sizeLabels[size]}
@@ -71,27 +73,13 @@ export default function CanvasSizeSelector({ canvasSize, onChange }: CanvasSizeS
71
  };
72
 
73
  return (
74
- <div style={{ display: 'flex', alignItems: 'center', gap: '10px' }}>
75
- <span style={{
76
- color: '#999999',
77
- fontSize: '16px',
78
- fontWeight: 'normal',
79
- fontFamily: 'Inter, sans-serif'
80
- }}>
81
  Size:
82
  </span>
83
 
84
  {/* Outer pill container */}
85
- <div style={{
86
- display: 'flex',
87
- alignItems: 'center',
88
- gap: '5px',
89
- height: '40px',
90
- padding: '4px',
91
- background: '#EDF0F2',
92
- border: '1px solid #F8F9FA',
93
- borderRadius: '99px'
94
- }}>
95
  {renderSizeButton('1200x675', IconXSize, '1200×675 (Default)')}
96
  {renderSizeButton('linkedin', IconLinkedInSize, 'LinkedIn size (1200x627)')}
97
  {renderSizeButton('hf', IconHFSize, 'HF custom size (1160x580)')}
 
1
+ import { useState, useEffect } from 'react';
2
  import { CanvasSize } from '../../types/canvas.types';
3
  import IconXSize from '../Icons/IconXSize';
4
  import IconLinkedInSize from '../Icons/IconLinkedInSize';
 
17
 
18
  export default function CanvasSizeSelector({ canvasSize, onChange }: CanvasSizeSelectorProps) {
19
  const [hoveredSize, setHoveredSize] = useState<CanvasSize | null>(null);
20
+ const [isLargeScreen, setIsLargeScreen] = useState(window.innerWidth >= 1024);
21
+
22
+ useEffect(() => {
23
+ const handleResize = () => {
24
+ setIsLargeScreen(window.innerWidth >= 1024);
25
+ };
26
+
27
+ window.addEventListener('resize', handleResize);
28
+ return () => window.removeEventListener('resize', handleResize);
29
+ }, []);
30
 
31
  const renderSizeButton = (
32
  size: CanvasSize,
 
41
  onClick={() => onChange(size)}
42
  onMouseEnter={() => setHoveredSize(size)}
43
  onMouseLeave={() => setHoveredSize(null)}
44
+ className="flex items-center border-none rounded-[99px] cursor-pointer overflow-hidden transition-[background,padding,min-width] duration-150 ease-in-out"
45
  style={{
 
 
46
  justifyContent: isSelected ? 'flex-start' : 'center',
47
+ gap: '4.5px',
48
+ height: isLargeScreen ? '32px' : '29px',
49
+ minWidth: isLargeScreen ? '38px' : '34px',
50
+ paddingLeft: isSelected ? (isLargeScreen ? '10px' : '9px') : (isLargeScreen ? '9px' : '8px'),
51
+ paddingRight: isSelected ? (isLargeScreen ? '10px' : '9px') : (isLargeScreen ? '9px' : '8px'),
52
+ background: isSelected ? '#DEE2E7' : (isHovered ? '#f0f2f4' : 'transparent')
 
 
 
 
 
53
  }}
54
  title={title}
55
  >
56
  <Icon selected={isSelected} />
57
  <span
58
+ className="whitespace-nowrap overflow-hidden transition-[opacity,transform] duration-150 ease-in-out"
59
  style={{
60
  color: '#32343d',
61
+ fontSize: isLargeScreen ? '16px' : '14px',
62
  fontWeight: 'normal',
63
  fontFamily: 'Inter, sans-serif',
 
64
  opacity: isSelected ? 0.6 : 0,
65
  transform: isSelected ? 'translateX(0)' : 'translateX(-10px)',
66
+ width: isSelected ? 'auto' : '0'
 
 
67
  }}
68
  >
69
  {sizeLabels[size]}
 
73
  };
74
 
75
  return (
76
+ <div className="flex items-center gap-[9px] lg:gap-[10px]">
77
+ <span className="text-[#999999] text-[14px] lg:text-[16px] font-normal" style={{ fontFamily: 'Inter, sans-serif' }}>
 
 
 
 
 
78
  Size:
79
  </span>
80
 
81
  {/* Outer pill container */}
82
+ <div className="flex items-center gap-[4.5px] lg:gap-[5px] h-[36px] lg:h-[40px] p-[3.6px] lg:p-[4px] bg-[#EDF0F2] border border-[#F8F9FA] rounded-[99px]">
 
 
 
 
 
 
 
 
 
83
  {renderSizeButton('1200x675', IconXSize, '1200×675 (Default)')}
84
  {renderSizeButton('linkedin', IconLinkedInSize, 'LinkedIn size (1200x627)')}
85
  {renderSizeButton('hf', IconHFSize, 'HF custom size (1160x580)')}
src/components/ExportButton/ExportButton.tsx CHANGED
@@ -58,17 +58,10 @@ export default function ExportButton({ onExport, isExporting = false, currentLay
58
 
59
  return (
60
  <div
 
61
  style={{
62
- position: 'fixed',
63
- top: '30px',
64
- right: '22px',
65
- zIndex: 50,
66
  backgroundColor: '#262933',
67
  borderRadius: '10px',
68
- padding: '5px',
69
- display: 'inline-flex',
70
- alignItems: 'stretch',
71
- gap: '5px',
72
  boxShadow: '0px 0px 0px 0px rgba(0,0,0,0), 0px 0px 0px 0px rgba(0,0,0,0.03), 0px 0px 6.417px 0px rgba(0,0,0,0.09), 0px 0px 4.583px 0px rgba(0,0,0,0.15), 0px 0px 2.75px 0px rgba(0,0,0,0.17), 0px 16.847px 21.059px -4.212px rgba(14,13,13,0.1), 0px 8.423px 8.423px -4.212px rgba(0,0,0,0.04)',
73
  opacity: isExporting ? 0.5 : 1,
74
  transition: 'all 0.2s ease-in-out',
@@ -81,40 +74,27 @@ export default function ExportButton({ onExport, isExporting = false, currentLay
81
  disabled={isExporting}
82
  onMouseEnter={() => setIsHoveringButton(true)}
83
  onMouseLeave={() => setIsHoveringButton(false)}
 
84
  style={{
85
- display: 'flex',
86
- alignItems: 'center',
87
- justifyContent: 'center',
88
  backgroundColor: isHoveringButton ? '#0d6ecc' : '#1888ff',
89
- borderRadius: '5px',
90
- width: '32px',
91
- aspectRatio: '1',
92
- flexShrink: 0,
93
- border: 'none',
94
- cursor: isExporting ? 'not-allowed' : 'pointer',
95
- padding: 0
96
  }}
97
  >
98
  {isExporting ? (
99
- <Loader2 size={16} color="white" style={{ animation: 'spin 1s linear infinite' }} />
100
  ) : (
101
- <Download size={16} color="white" />
102
  )}
103
  </button>
104
 
105
  {/* Filename Container */}
106
- <div style={{ display: 'flex', alignItems: 'center', gap: '2px', paddingRight: '5px', minHeight: '32px' }}>
107
  {/* Hidden span to measure text width */}
108
  <span
109
  ref={spanRef}
 
110
  style={{
111
- position: 'absolute',
112
- visibility: 'hidden',
113
- whiteSpace: 'pre',
114
- fontSize: '16px',
115
- fontFamily: 'Inter, sans-serif',
116
- fontWeight: 'normal',
117
- padding: '5px'
118
  }}
119
  >
120
  {filename || 'thumbnail_name'}
@@ -128,16 +108,9 @@ export default function ExportButton({ onExport, isExporting = false, currentLay
128
  onBlur={() => setIsFocused(false)}
129
  disabled={isExporting}
130
  placeholder="thumbnail_name"
 
131
  style={{
132
- backgroundColor: 'rgba(255, 255, 255, 0.05)',
133
- color: 'white',
134
- fontSize: '16px',
135
  fontFamily: 'Inter, sans-serif',
136
- fontWeight: 'normal',
137
- outline: 'none',
138
- border: 'none',
139
- padding: '5px',
140
- borderRadius: '4px',
141
  width: `${inputWidth}px`,
142
  cursor: isExporting ? 'not-allowed' : 'text',
143
  opacity: isFocused ? 1 : 0.5,
@@ -145,7 +118,7 @@ export default function ExportButton({ onExport, isExporting = false, currentLay
145
  }}
146
  onClick={(e) => e.stopPropagation()}
147
  />
148
- <span style={{ color: 'white', fontSize: '16px', fontFamily: 'Inter, sans-serif', fontWeight: 'normal' }}>
149
  .png
150
  </span>
151
  </div>
 
58
 
59
  return (
60
  <div
61
+ className="fixed top-[27px] lg:top-[30px] right-[20px] lg:right-[22px] z-50 p-[4px] lg:p-[5px] inline-flex items-stretch gap-[4px] lg:gap-[5px]"
62
  style={{
 
 
 
 
63
  backgroundColor: '#262933',
64
  borderRadius: '10px',
 
 
 
 
65
  boxShadow: '0px 0px 0px 0px rgba(0,0,0,0), 0px 0px 0px 0px rgba(0,0,0,0.03), 0px 0px 6.417px 0px rgba(0,0,0,0.09), 0px 0px 4.583px 0px rgba(0,0,0,0.15), 0px 0px 2.75px 0px rgba(0,0,0,0.17), 0px 16.847px 21.059px -4.212px rgba(14,13,13,0.1), 0px 8.423px 8.423px -4.212px rgba(0,0,0,0.04)',
66
  opacity: isExporting ? 0.5 : 1,
67
  transition: 'all 0.2s ease-in-out',
 
74
  disabled={isExporting}
75
  onMouseEnter={() => setIsHoveringButton(true)}
76
  onMouseLeave={() => setIsHoveringButton(false)}
77
+ className="flex items-center justify-center rounded-[5px] w-[29px] lg:w-[32px] aspect-square flex-shrink-0 border-none p-0"
78
  style={{
 
 
 
79
  backgroundColor: isHoveringButton ? '#0d6ecc' : '#1888ff',
80
+ cursor: isExporting ? 'not-allowed' : 'pointer'
 
 
 
 
 
 
81
  }}
82
  >
83
  {isExporting ? (
84
+ <Loader2 className="w-[14px] lg:w-4 h-[14px] lg:h-4" color="white" style={{ animation: 'spin 1s linear infinite' }} />
85
  ) : (
86
+ <Download className="w-[14px] lg:w-4 h-[14px] lg:h-4" color="white" />
87
  )}
88
  </button>
89
 
90
  {/* Filename Container */}
91
+ <div className="flex items-center gap-[2px] pr-[4px] lg:pr-[5px] min-h-[29px] lg:min-h-[32px]">
92
  {/* Hidden span to measure text width */}
93
  <span
94
  ref={spanRef}
95
+ className="absolute invisible whitespace-pre text-[14px] lg:text-[16px] font-normal p-[4px] lg:p-[5px]"
96
  style={{
97
+ fontFamily: 'Inter, sans-serif'
 
 
 
 
 
 
98
  }}
99
  >
100
  {filename || 'thumbnail_name'}
 
108
  onBlur={() => setIsFocused(false)}
109
  disabled={isExporting}
110
  placeholder="thumbnail_name"
111
+ className="bg-white/5 text-white text-[14px] lg:text-[16px] font-normal outline-none border-none p-[4px] lg:p-[5px] rounded"
112
  style={{
 
 
 
113
  fontFamily: 'Inter, sans-serif',
 
 
 
 
 
114
  width: `${inputWidth}px`,
115
  cursor: isExporting ? 'not-allowed' : 'text',
116
  opacity: isFocused ? 1 : 0.5,
 
118
  }}
119
  onClick={(e) => e.stopPropagation()}
120
  />
121
+ <span className="text-white text-[14px] lg:text-[16px] font-normal" style={{ fontFamily: 'Inter, sans-serif' }}>
122
  .png
123
  </span>
124
  </div>
src/components/Sidebar/Sidebar.tsx CHANGED
@@ -42,65 +42,65 @@ export default function Sidebar({
42
  e.stopPropagation();
43
  }}
44
  >
45
- <div className="sidebar-container bg-[#f8f9fa] border border-[#D7DCE2] rounded-[12px] p-[5px] flex flex-col gap-[15px] w-[87px] relative">
46
  {/* Layout Button */}
47
  <button
48
  onClick={onLayoutClick}
49
- className={`flex flex-col items-center gap-[3px] px-[13px] py-[5px] rounded-[7px] transition-colors ${
50
  activeButton === 'layout'
51
  ? 'bg-[#3faee6]'
52
  : 'hover:bg-[#e9ecef]'
53
  }`}
54
  >
55
- <div className="w-[32px] h-[32px] flex items-center justify-center">
56
  <IconLayout selected={activeButton === 'layout'} />
57
  </div>
58
- <p className={`text-[16px] font-normal ${activeButton === 'layout' ? 'text-white' : 'text-[#545865]'}`}>Layout</p>
59
  </button>
60
 
61
  {/* Huggy Button */}
62
  <button
63
  onClick={onHuggyClick}
64
- className={`flex flex-col items-center gap-[3px] px-[13px] py-[5px] rounded-[7px] transition-colors ${
65
  activeButton === 'huggy'
66
  ? 'bg-[#3faee6]'
67
  : 'hover:bg-[#e9ecef]'
68
  }`}
69
  >
70
- <div className="w-[32px] h-[32px] flex items-center justify-center">
71
  <IconHuggy />
72
  </div>
73
- <p className={`text-[16px] font-normal ${activeButton === 'huggy' ? 'text-white' : 'text-[#545865]'}`}>Huggy</p>
74
  </button>
75
 
76
  {/* Image Button */}
77
  <button
78
  onClick={onImageClick}
79
- className={`flex flex-col items-center gap-[3px] px-[13px] py-[5px] rounded-[7px] transition-colors ${
80
  activeButton === 'image'
81
  ? 'bg-[#3faee6]'
82
  : 'hover:bg-[#e9ecef]'
83
  }`}
84
  >
85
- <div className="w-[32px] h-[32px] flex items-center justify-center">
86
  <IconImage selected={activeButton === 'image'} />
87
  </div>
88
- <p className={`text-[16px] font-normal ${activeButton === 'image' ? 'text-white' : 'text-[#545865]'}`}>Image</p>
89
  </button>
90
 
91
  {/* Text Button */}
92
  <button
93
  onClick={onTextClick}
94
- className={`flex flex-col items-center gap-[3px] px-[13px] py-[5px] rounded-[7px] transition-colors ${
95
  activeButton === 'text'
96
  ? 'bg-[#3faee6]'
97
  : 'hover:bg-[#e9ecef]'
98
  }`}
99
  >
100
- <div className="w-[32px] h-[32px] flex items-center justify-center">
101
  <IconText selected={activeButton === 'text'} />
102
  </div>
103
- <p className={`text-[16px] font-normal ${activeButton === 'text' ? 'text-white' : 'text-[#545865]'}`}>Text</p>
104
  </button>
105
 
106
  {/* Layout Selector - positioned absolutely, aligned with Layout button */}
@@ -118,9 +118,9 @@ export default function Sidebar({
118
 
119
  {/* Text Hint Box - positioned absolutely, aligned with Text button */}
120
  {activeButton === 'text' && (
121
- <div className="text-hint absolute left-[calc(100%+4px)] bottom-[5px] bg-[#3faee6] rounded-[10px] p-[10px] w-[233px]">
122
- <p className="text-white text-[16px] font-normal leading-normal">
123
- Click anywhere on the canvas to add texts
124
  </p>
125
  </div>
126
  )}
 
42
  e.stopPropagation();
43
  }}
44
  >
45
+ <div className="sidebar-container bg-[#f8f9fa] border border-[#D7DCE2] rounded-[12px] p-[5px] flex flex-col gap-[14px] lg:gap-[15px] w-[78px] lg:w-[87px] relative">
46
  {/* Layout Button */}
47
  <button
48
  onClick={onLayoutClick}
49
+ className={`flex flex-col items-center gap-[3px] px-[12px] lg:px-[13px] py-[5px] rounded-[7px] transition-colors ${
50
  activeButton === 'layout'
51
  ? 'bg-[#3faee6]'
52
  : 'hover:bg-[#e9ecef]'
53
  }`}
54
  >
55
+ <div className="w-[29px] lg:w-[32px] h-[29px] lg:h-[32px] flex items-center justify-center">
56
  <IconLayout selected={activeButton === 'layout'} />
57
  </div>
58
+ <p className={`text-[14px] lg:text-[16px] font-normal ${activeButton === 'layout' ? 'text-white' : 'text-[#545865]'}`}>Layout</p>
59
  </button>
60
 
61
  {/* Huggy Button */}
62
  <button
63
  onClick={onHuggyClick}
64
+ className={`flex flex-col items-center gap-[3px] px-[12px] lg:px-[13px] py-[5px] rounded-[7px] transition-colors ${
65
  activeButton === 'huggy'
66
  ? 'bg-[#3faee6]'
67
  : 'hover:bg-[#e9ecef]'
68
  }`}
69
  >
70
+ <div className="w-[29px] lg:w-[32px] h-[29px] lg:h-[32px] flex items-center justify-center">
71
  <IconHuggy />
72
  </div>
73
+ <p className={`text-[14px] lg:text-[16px] font-normal ${activeButton === 'huggy' ? 'text-white' : 'text-[#545865]'}`}>Huggy</p>
74
  </button>
75
 
76
  {/* Image Button */}
77
  <button
78
  onClick={onImageClick}
79
+ className={`flex flex-col items-center gap-[3px] px-[12px] lg:px-[13px] py-[5px] rounded-[7px] transition-colors ${
80
  activeButton === 'image'
81
  ? 'bg-[#3faee6]'
82
  : 'hover:bg-[#e9ecef]'
83
  }`}
84
  >
85
+ <div className="w-[29px] lg:w-[32px] h-[29px] lg:h-[32px] flex items-center justify-center">
86
  <IconImage selected={activeButton === 'image'} />
87
  </div>
88
+ <p className={`text-[14px] lg:text-[16px] font-normal ${activeButton === 'image' ? 'text-white' : 'text-[#545865]'}`}>Image</p>
89
  </button>
90
 
91
  {/* Text Button */}
92
  <button
93
  onClick={onTextClick}
94
+ className={`flex flex-col items-center gap-[3px] px-[12px] lg:px-[13px] py-[5px] rounded-[7px] transition-colors ${
95
  activeButton === 'text'
96
  ? 'bg-[#3faee6]'
97
  : 'hover:bg-[#e9ecef]'
98
  }`}
99
  >
100
+ <div className="w-[29px] lg:w-[32px] h-[29px] lg:h-[32px] flex items-center justify-center">
101
  <IconText selected={activeButton === 'text'} />
102
  </div>
103
+ <p className={`text-[14px] lg:text-[16px] font-normal ${activeButton === 'text' ? 'text-white' : 'text-[#545865]'}`}>Text</p>
104
  </button>
105
 
106
  {/* Layout Selector - positioned absolutely, aligned with Layout button */}
 
118
 
119
  {/* Text Hint Box - positioned absolutely, aligned with Text button */}
120
  {activeButton === 'text' && (
121
+ <div className="text-hint absolute left-[calc(100%+4px)] bottom-[5px] bg-[#3faee6] rounded-[10px] p-[9px] lg:p-[10px] w-max">
122
+ <p className="text-white text-[14px] lg:text-[16px] font-normal leading-normal">
123
+ Click anywhere on the<br />canvas to add texts
124
  </p>
125
  </div>
126
  )}
src/components/TextToolbar/ColorPicker.tsx CHANGED
@@ -201,7 +201,6 @@ export default function ColorPicker({ color, onChange, onClose }: ColorPickerPro
201
  onChange(pickedColor);
202
  } catch (error) {
203
  // User cancelled the eyedropper
204
- console.log('Eyedropper cancelled');
205
  }
206
  };
207
 
 
201
  onChange(pickedColor);
202
  } catch (error) {
203
  // User cancelled the eyedropper
 
204
  }
205
  };
206
 
src/components/TextToolbar/TextToolbar.tsx CHANGED
@@ -38,6 +38,7 @@ export default function TextToolbar({
38
  const [showFontDropdown, setShowFontDropdown] = useState(false);
39
  const [showColorPicker, setShowColorPicker] = useState(false);
40
  const [dropdownDirection, setDropdownDirection] = useState<'down' | 'up'>('down');
 
41
  const dropdownButtonRef = useRef<HTMLButtonElement>(null);
42
 
43
  const fonts: Array<'Inter' | 'IBM Plex Mono' | 'Bison' | 'Source Sans 3'> = ['Inter', 'IBM Plex Mono', 'Bison', 'Source Sans 3'];
@@ -54,6 +55,16 @@ export default function TextToolbar({
54
  const hasItalic = fontVariations[fontFamily].italic;
55
  const hasBlack = fontVariations[fontFamily].black;
56
 
 
 
 
 
 
 
 
 
 
 
57
  // Check dropdown position when it opens
58
  useEffect(() => {
59
  if (showFontDropdown && dropdownButtonRef.current) {
@@ -81,6 +92,10 @@ export default function TextToolbar({
81
  const rightPosition = `calc((100vw - ${scaledWidth}px) / 2)`;
82
  const bottomPosition = `calc((100vh - ${scaledHeight}px - ${scaledOverhead}px) / 2 - 10px - 44px)`;
83
 
 
 
 
 
84
  return (
85
  <div
86
  className="text-toolbar"
@@ -98,35 +113,38 @@ export default function TextToolbar({
98
  bottom: bottomPosition,
99
  zIndex: 100,
100
  backgroundColor: '#27272A',
101
- borderRadius: '8px',
102
- padding: '4px',
103
  boxShadow: '0 4px 6px -2px rgba(0, 0, 0, 0.1), 0 12px 16px -4px rgba(0, 0, 0, 0.17)',
104
  transition: 'right 0.15s ease-in-out, bottom 0.15s ease-in-out'
105
  }}
106
  >
107
- <div style={{ display: 'flex', alignItems: 'center', gap: '4px' }}>
108
  {/* Font Family Dropdown */}
109
  <div style={{ position: 'relative' }}>
110
  <button
111
  ref={dropdownButtonRef}
112
- onClick={() => setShowFontDropdown(!showFontDropdown)}
 
 
 
113
  style={{
114
  display: 'flex',
115
  alignItems: 'center',
116
- gap: '20px',
117
- padding: '8px',
118
  backgroundColor: 'rgba(255, 255, 255, 0.05)',
119
  border: 'none',
120
  borderRadius: '4px',
121
  color: 'white',
122
- fontSize: '16px',
123
  fontFamily: 'Source Sans Pro, sans-serif',
124
  cursor: 'pointer',
125
  whiteSpace: 'nowrap'
126
  }}
127
  >
128
  {fontFamily}
129
- <ChevronDown size={14} style={{
130
  transform: showFontDropdown && dropdownDirection === 'up' ? 'rotate(180deg)' : 'none',
131
  transition: 'transform 0.15s ease-in-out'
132
  }} />
@@ -137,15 +155,15 @@ export default function TextToolbar({
137
  style={{
138
  position: 'absolute',
139
  ...(dropdownDirection === 'down'
140
- ? { top: 'calc(100% + 4px)' }
141
- : { bottom: 'calc(100% + 4px)' }
142
  ),
143
  left: 0,
144
  backgroundColor: '#27272A',
145
  border: '1px solid #09090B',
146
- borderRadius: '8px',
147
- padding: '4px',
148
- minWidth: '160px',
149
  zIndex: 1000
150
  }}
151
  >
@@ -159,12 +177,12 @@ export default function TextToolbar({
159
  style={{
160
  display: 'block',
161
  width: '100%',
162
- padding: '8px 12px',
163
  backgroundColor: fontFamily === font ? 'rgba(255, 255, 255, 0.1)' : 'transparent',
164
  border: 'none',
165
  borderRadius: '4px',
166
  color: 'white',
167
- fontSize: '14px',
168
  fontFamily: font,
169
  textAlign: 'left',
170
  cursor: 'pointer'
@@ -191,7 +209,7 @@ export default function TextToolbar({
191
  <div
192
  style={{
193
  width: '1px',
194
- height: '18px',
195
  backgroundColor: 'rgba(255, 255, 255, 0.2)'
196
  }}
197
  />
@@ -199,12 +217,15 @@ export default function TextToolbar({
199
  {/* Color Picker Button */}
200
  <div style={{ position: 'relative' }}>
201
  <button
202
- onClick={() => setShowColorPicker(!showColorPicker)}
 
 
 
203
  style={{
204
  display: 'flex',
205
  alignItems: 'center',
206
  justifyContent: 'center',
207
- padding: '10px',
208
  backgroundColor: 'transparent',
209
  border: 'none',
210
  borderRadius: '99px',
@@ -213,8 +234,8 @@ export default function TextToolbar({
213
  >
214
  <div
215
  style={{
216
- width: '16px',
217
- height: '16px',
218
  borderRadius: '999px',
219
  backgroundColor: fill,
220
  border: '1px solid #e5e9ed'
@@ -234,14 +255,18 @@ export default function TextToolbar({
234
 
235
  {/* Bold Button */}
236
  <button
237
- onClick={hasBold ? onBoldToggle : undefined}
 
 
 
 
238
  disabled={!hasBold}
239
  style={{
240
  position: 'relative',
241
  display: 'flex',
242
  alignItems: 'center',
243
  justifyContent: 'center',
244
- padding: '8px',
245
  backgroundColor: bold || fontWeight === 'black' ? 'rgba(255, 255, 255, 0.1)' : 'transparent',
246
  border: 'none',
247
  borderRadius: '4px',
@@ -250,15 +275,15 @@ export default function TextToolbar({
250
  opacity: hasBold ? 1 : 0.3
251
  }}
252
  >
253
- <Bold size={20} />
254
  {/* Show indicator for BLACK weight */}
255
  {hasBlack && fontWeight === 'black' && (
256
  <div style={{
257
  position: 'absolute',
258
  bottom: '2px',
259
  right: '2px',
260
- width: '6px',
261
- height: '6px',
262
  borderRadius: '50%',
263
  backgroundColor: '#3faee6'
264
  }} />
@@ -267,13 +292,17 @@ export default function TextToolbar({
267
 
268
  {/* Italic Button */}
269
  <button
270
- onClick={hasItalic ? onItalicToggle : undefined}
 
 
 
 
271
  disabled={!hasItalic}
272
  style={{
273
  display: 'flex',
274
  alignItems: 'center',
275
  justifyContent: 'center',
276
- padding: '8px',
277
  backgroundColor: italic ? 'rgba(255, 255, 255, 0.1)' : 'transparent',
278
  border: 'none',
279
  borderRadius: '4px',
@@ -282,7 +311,7 @@ export default function TextToolbar({
282
  opacity: hasItalic ? 1 : 0.3
283
  }}
284
  >
285
- <Italic size={20} />
286
  </button>
287
  </div>
288
  </div>
 
38
  const [showFontDropdown, setShowFontDropdown] = useState(false);
39
  const [showColorPicker, setShowColorPicker] = useState(false);
40
  const [dropdownDirection, setDropdownDirection] = useState<'down' | 'up'>('down');
41
+ const [isLargeScreen, setIsLargeScreen] = useState(window.innerWidth >= 1024);
42
  const dropdownButtonRef = useRef<HTMLButtonElement>(null);
43
 
44
  const fonts: Array<'Inter' | 'IBM Plex Mono' | 'Bison' | 'Source Sans 3'> = ['Inter', 'IBM Plex Mono', 'Bison', 'Source Sans 3'];
 
55
  const hasItalic = fontVariations[fontFamily].italic;
56
  const hasBlack = fontVariations[fontFamily].black;
57
 
58
+ // Handle window resize for responsive behavior
59
+ useEffect(() => {
60
+ const handleResize = () => {
61
+ setIsLargeScreen(window.innerWidth >= 1024);
62
+ };
63
+
64
+ window.addEventListener('resize', handleResize);
65
+ return () => window.removeEventListener('resize', handleResize);
66
+ }, []);
67
+
68
  // Check dropdown position when it opens
69
  useEffect(() => {
70
  if (showFontDropdown && dropdownButtonRef.current) {
 
92
  const rightPosition = `calc((100vw - ${scaledWidth}px) / 2)`;
93
  const bottomPosition = `calc((100vh - ${scaledHeight}px - ${scaledOverhead}px) / 2 - 10px - 44px)`;
94
 
95
+ // Responsive sizing values
96
+ const toolbarPadding = isLargeScreen ? '4px' : '3.6px';
97
+ const buttonGap = isLargeScreen ? '4px' : '3.6px';
98
+
99
  return (
100
  <div
101
  className="text-toolbar"
 
113
  bottom: bottomPosition,
114
  zIndex: 100,
115
  backgroundColor: '#27272A',
116
+ borderRadius: isLargeScreen ? '8px' : '7px',
117
+ padding: toolbarPadding,
118
  boxShadow: '0 4px 6px -2px rgba(0, 0, 0, 0.1), 0 12px 16px -4px rgba(0, 0, 0, 0.17)',
119
  transition: 'right 0.15s ease-in-out, bottom 0.15s ease-in-out'
120
  }}
121
  >
122
+ <div style={{ display: 'flex', alignItems: 'center', gap: buttonGap }}>
123
  {/* Font Family Dropdown */}
124
  <div style={{ position: 'relative' }}>
125
  <button
126
  ref={dropdownButtonRef}
127
+ onClick={() => {
128
+ setShowFontDropdown(!showFontDropdown);
129
+ setShowColorPicker(false);
130
+ }}
131
  style={{
132
  display: 'flex',
133
  alignItems: 'center',
134
+ gap: isLargeScreen ? '20px' : '18px',
135
+ padding: isLargeScreen ? '8px' : '7px',
136
  backgroundColor: 'rgba(255, 255, 255, 0.05)',
137
  border: 'none',
138
  borderRadius: '4px',
139
  color: 'white',
140
+ fontSize: isLargeScreen ? '16px' : '14px',
141
  fontFamily: 'Source Sans Pro, sans-serif',
142
  cursor: 'pointer',
143
  whiteSpace: 'nowrap'
144
  }}
145
  >
146
  {fontFamily}
147
+ <ChevronDown size={isLargeScreen ? 14 : 13} style={{
148
  transform: showFontDropdown && dropdownDirection === 'up' ? 'rotate(180deg)' : 'none',
149
  transition: 'transform 0.15s ease-in-out'
150
  }} />
 
155
  style={{
156
  position: 'absolute',
157
  ...(dropdownDirection === 'down'
158
+ ? { top: isLargeScreen ? 'calc(100% + 4px)' : 'calc(100% + 3.6px)' }
159
+ : { bottom: isLargeScreen ? 'calc(100% + 4px)' : 'calc(100% + 3.6px)' }
160
  ),
161
  left: 0,
162
  backgroundColor: '#27272A',
163
  border: '1px solid #09090B',
164
+ borderRadius: isLargeScreen ? '8px' : '7px',
165
+ padding: isLargeScreen ? '4px' : '3.6px',
166
+ minWidth: isLargeScreen ? '160px' : '144px',
167
  zIndex: 1000
168
  }}
169
  >
 
177
  style={{
178
  display: 'block',
179
  width: '100%',
180
+ padding: isLargeScreen ? '8px 12px' : '7px 11px',
181
  backgroundColor: fontFamily === font ? 'rgba(255, 255, 255, 0.1)' : 'transparent',
182
  border: 'none',
183
  borderRadius: '4px',
184
  color: 'white',
185
+ fontSize: isLargeScreen ? '14px' : '13px',
186
  fontFamily: font,
187
  textAlign: 'left',
188
  cursor: 'pointer'
 
209
  <div
210
  style={{
211
  width: '1px',
212
+ height: isLargeScreen ? '18px' : '16px',
213
  backgroundColor: 'rgba(255, 255, 255, 0.2)'
214
  }}
215
  />
 
217
  {/* Color Picker Button */}
218
  <div style={{ position: 'relative' }}>
219
  <button
220
+ onClick={() => {
221
+ setShowColorPicker(!showColorPicker);
222
+ setShowFontDropdown(false);
223
+ }}
224
  style={{
225
  display: 'flex',
226
  alignItems: 'center',
227
  justifyContent: 'center',
228
+ padding: isLargeScreen ? '10px' : '9px',
229
  backgroundColor: 'transparent',
230
  border: 'none',
231
  borderRadius: '99px',
 
234
  >
235
  <div
236
  style={{
237
+ width: isLargeScreen ? '16px' : '14px',
238
+ height: isLargeScreen ? '16px' : '14px',
239
  borderRadius: '999px',
240
  backgroundColor: fill,
241
  border: '1px solid #e5e9ed'
 
255
 
256
  {/* Bold Button */}
257
  <button
258
+ onClick={hasBold ? () => {
259
+ onBoldToggle();
260
+ setShowFontDropdown(false);
261
+ setShowColorPicker(false);
262
+ } : undefined}
263
  disabled={!hasBold}
264
  style={{
265
  position: 'relative',
266
  display: 'flex',
267
  alignItems: 'center',
268
  justifyContent: 'center',
269
+ padding: isLargeScreen ? '8px' : '7px',
270
  backgroundColor: bold || fontWeight === 'black' ? 'rgba(255, 255, 255, 0.1)' : 'transparent',
271
  border: 'none',
272
  borderRadius: '4px',
 
275
  opacity: hasBold ? 1 : 0.3
276
  }}
277
  >
278
+ <Bold size={isLargeScreen ? 20 : 18} />
279
  {/* Show indicator for BLACK weight */}
280
  {hasBlack && fontWeight === 'black' && (
281
  <div style={{
282
  position: 'absolute',
283
  bottom: '2px',
284
  right: '2px',
285
+ width: isLargeScreen ? '6px' : '5px',
286
+ height: isLargeScreen ? '6px' : '5px',
287
  borderRadius: '50%',
288
  backgroundColor: '#3faee6'
289
  }} />
 
292
 
293
  {/* Italic Button */}
294
  <button
295
+ onClick={hasItalic ? () => {
296
+ onItalicToggle();
297
+ setShowFontDropdown(false);
298
+ setShowColorPicker(false);
299
+ } : undefined}
300
  disabled={!hasItalic}
301
  style={{
302
  display: 'flex',
303
  alignItems: 'center',
304
  justifyContent: 'center',
305
+ padding: isLargeScreen ? '8px' : '7px',
306
  backgroundColor: italic ? 'rgba(255, 255, 255, 0.1)' : 'transparent',
307
  border: 'none',
308
  borderRadius: '4px',
 
311
  opacity: hasItalic ? 1 : 0.3
312
  }}
313
  >
314
+ <Italic size={isLargeScreen ? 20 : 18} />
315
  </button>
316
  </div>
317
  </div>
src/index.css CHANGED
@@ -4,10 +4,10 @@
4
 
5
  @font-face {
6
  font-family: 'Bison';
7
- src: url('/fonts/Bison-Bold(PersonalUse).ttf') format('truetype');
8
  font-weight: bold;
9
  font-style: normal;
10
- font-display: block;
11
  }
12
 
13
  body {
 
4
 
5
  @font-face {
6
  font-family: 'Bison';
7
+ src: url('/fonts/Bison-Bold.ttf') format('truetype');
8
  font-weight: bold;
9
  font-style: normal;
10
+ font-display: swap;
11
  }
12
 
13
  body {