Skip to content

Commit b05f5b3

Browse files
ppippi-devclaude
andcommitted
feat(blog): improve code copy button with icon and positioning
- Fix CSS scoping issue with :global() for dynamic elements - Replace text labels with SVG icons (clipboard/check) - Position copy button at top-right corner of code blocks Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 9ae9cba commit b05f5b3

File tree

2 files changed

+24
-20
lines changed

2 files changed

+24
-20
lines changed

src/layouts/BlogPost.astro

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -340,10 +340,10 @@ const imageUrl = heroImage ? new URL(heroImage, Astro.site).href : new URL(Fallb
340340
font-size: 0.85rem;
341341
flex-shrink: 0;
342342
}
343-
.code-block-wrapper {
343+
:global(.code-block-wrapper) {
344344
position: relative;
345345
}
346-
.copy-button {
346+
:global(.copy-button) {
347347
position: absolute;
348348
top: 8px;
349349
right: 8px;
@@ -357,14 +357,14 @@ const imageUrl = heroImage ? new URL(heroImage, Astro.site).href : new URL(Fallb
357357
opacity: 0;
358358
transition: opacity 0.2s, background 0.2s;
359359
}
360-
.code-block-wrapper:hover .copy-button {
360+
:global(.code-block-wrapper:hover .copy-button) {
361361
opacity: 1;
362362
}
363-
.copy-button:hover {
363+
:global(.copy-button:hover) {
364364
background: rgba(255, 255, 255, 0.2);
365365
color: #fff;
366366
}
367-
.copy-button.copied {
367+
:global(.copy-button.copied) {
368368
background: #35C5F0;
369369
border-color: #35C5F0;
370370
color: #fff;
@@ -596,6 +596,9 @@ const imageUrl = heroImage ? new URL(heroImage, Astro.site).href : new URL(Fallb
596596
})();
597597

598598
// Add copy button to code blocks
599+
const clipboardIcon = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>`;
600+
const checkIcon = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"></polyline></svg>`;
601+
599602
document.querySelectorAll('.post-content pre').forEach(pre => {
600603
// Wrap pre in a div for positioning
601604
const wrapper = document.createElement('div');
@@ -606,23 +609,22 @@ const imageUrl = heroImage ? new URL(heroImage, Astro.site).href : new URL(Fallb
606609
// Create copy button
607610
const button = document.createElement('button');
608611
button.className = 'copy-button';
609-
button.textContent = '복사';
612+
button.innerHTML = clipboardIcon;
610613
button.setAttribute('aria-label', '코드 복사');
611614

612615
button.addEventListener('click', async () => {
613616
const code = pre.querySelector('code')?.textContent || pre.textContent || '';
614617
try {
615618
await navigator.clipboard.writeText(code);
616-
button.textContent = '복사됨!';
619+
button.innerHTML = checkIcon;
617620
button.classList.add('copied');
618621
setTimeout(() => {
619-
button.textContent = '복사';
622+
button.innerHTML = clipboardIcon;
620623
button.classList.remove('copied');
621624
}, 2000);
622625
} catch (err) {
623-
button.textContent = '실패';
624626
setTimeout(() => {
625-
button.textContent = '복사';
627+
button.innerHTML = clipboardIcon;
626628
}, 2000);
627629
}
628630
});

src/layouts/BlogPostEn.astro

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -348,10 +348,10 @@ const imageUrl = heroImage ? new URL(heroImage, Astro.site).href : new URL(Fallb
348348
font-size: 0.85rem;
349349
flex-shrink: 0;
350350
}
351-
.code-block-wrapper {
351+
:global(.code-block-wrapper) {
352352
position: relative;
353353
}
354-
.copy-button {
354+
:global(.copy-button) {
355355
position: absolute;
356356
top: 8px;
357357
right: 8px;
@@ -365,14 +365,14 @@ const imageUrl = heroImage ? new URL(heroImage, Astro.site).href : new URL(Fallb
365365
opacity: 0;
366366
transition: opacity 0.2s, background 0.2s;
367367
}
368-
.code-block-wrapper:hover .copy-button {
368+
:global(.code-block-wrapper:hover .copy-button) {
369369
opacity: 1;
370370
}
371-
.copy-button:hover {
371+
:global(.copy-button:hover) {
372372
background: rgba(255, 255, 255, 0.2);
373373
color: #fff;
374374
}
375-
.copy-button.copied {
375+
:global(.copy-button.copied) {
376376
background: #35C5F0;
377377
border-color: #35C5F0;
378378
color: #fff;
@@ -607,6 +607,9 @@ const imageUrl = heroImage ? new URL(heroImage, Astro.site).href : new URL(Fallb
607607
})();
608608

609609
// Add copy button to code blocks
610+
const clipboardIcon = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>`;
611+
const checkIcon = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"></polyline></svg>`;
612+
610613
document.querySelectorAll('.post-content pre').forEach(pre => {
611614
// Wrap pre in a div for positioning
612615
const wrapper = document.createElement('div');
@@ -617,23 +620,22 @@ const imageUrl = heroImage ? new URL(heroImage, Astro.site).href : new URL(Fallb
617620
// Create copy button
618621
const button = document.createElement('button');
619622
button.className = 'copy-button';
620-
button.textContent = 'Copy';
623+
button.innerHTML = clipboardIcon;
621624
button.setAttribute('aria-label', 'Copy code');
622625

623626
button.addEventListener('click', async () => {
624627
const code = pre.querySelector('code')?.textContent || pre.textContent || '';
625628
try {
626629
await navigator.clipboard.writeText(code);
627-
button.textContent = 'Copied!';
630+
button.innerHTML = checkIcon;
628631
button.classList.add('copied');
629632
setTimeout(() => {
630-
button.textContent = 'Copy';
633+
button.innerHTML = clipboardIcon;
631634
button.classList.remove('copied');
632635
}, 2000);
633636
} catch (err) {
634-
button.textContent = 'Failed';
635637
setTimeout(() => {
636-
button.textContent = 'Copy';
638+
button.innerHTML = clipboardIcon;
637639
}, 2000);
638640
}
639641
});

0 commit comments

Comments
 (0)