Skip to content

Commit 489f75e

Browse files
committed
Try new design
1 parent 8267c4e commit 489f75e

File tree

7 files changed

+2409
-18
lines changed

7 files changed

+2409
-18
lines changed
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
<script setup lang="ts">
2+
import { onUnmounted, ref } from 'vue'
3+
4+
const props = defineProps<{
5+
code: string
6+
iconOnly?: boolean
7+
}>()
8+
9+
const copied = ref(false)
10+
const announcement = ref('')
11+
let timeoutId: ReturnType<typeof setTimeout> | null = null
12+
13+
const copyCode = async () => {
14+
// Cancel any pending timeout to prevent race conditions
15+
if (timeoutId) {
16+
clearTimeout(timeoutId)
17+
timeoutId = null
18+
}
19+
20+
try {
21+
await navigator.clipboard.writeText(props.code)
22+
copied.value = true
23+
announcement.value = 'Code copied to clipboard'
24+
25+
timeoutId = setTimeout(() => {
26+
copied.value = false
27+
announcement.value = ''
28+
timeoutId = null
29+
}, 2000)
30+
} catch {
31+
announcement.value = 'Failed to copy code'
32+
// Reset error message after delay
33+
timeoutId = setTimeout(() => {
34+
announcement.value = ''
35+
timeoutId = null
36+
}, 2000)
37+
}
38+
}
39+
40+
onUnmounted(() => {
41+
if (timeoutId) {
42+
clearTimeout(timeoutId)
43+
}
44+
})
45+
</script>
46+
47+
<template>
48+
<button
49+
type="button"
50+
class="copy-button"
51+
:class="{ copied, 'icon-only': iconOnly }"
52+
:aria-label="copied ? 'Copied to clipboard' : 'Copy code to clipboard'"
53+
@click="copyCode"
54+
>
55+
<svg
56+
v-if="copied"
57+
class="icon"
58+
viewBox="0 0 24 24"
59+
fill="none"
60+
stroke="currentColor"
61+
stroke-width="2"
62+
stroke-linecap="round"
63+
stroke-linejoin="round"
64+
aria-hidden="true"
65+
>
66+
<polyline points="20 6 9 17 4 12" />
67+
</svg>
68+
<svg
69+
v-else
70+
class="icon"
71+
viewBox="0 0 24 24"
72+
fill="none"
73+
stroke="currentColor"
74+
stroke-width="2"
75+
stroke-linecap="round"
76+
stroke-linejoin="round"
77+
aria-hidden="true"
78+
>
79+
<rect x="9" y="9" width="13" height="13" rx="2" ry="2" />
80+
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
81+
</svg>
82+
<span v-if="!iconOnly" class="label">{{
83+
copied ? 'Copied!' : 'Copy'
84+
}}</span>
85+
</button>
86+
87+
<!-- ARIA live region for screen reader announcements -->
88+
<div aria-live="polite" aria-atomic="true" class="sr-only">
89+
{{ announcement }}
90+
</div>
91+
</template>
92+
93+
<style scoped>
94+
.copy-button {
95+
display: flex;
96+
align-items: center;
97+
gap: 0.375rem;
98+
padding: 0.375rem 0.625rem;
99+
border: 1px solid var(--landing-border, rgba(255, 255, 255, 0.06));
100+
border-radius: 0.5rem;
101+
background: var(--landing-surface, rgba(17, 17, 19, 0.8));
102+
color: var(--landing-text-muted, #52525b);
103+
cursor: pointer;
104+
font-size: 0.75rem;
105+
font-family: inherit;
106+
backdrop-filter: blur(8px);
107+
transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1);
108+
}
109+
110+
.copy-button:hover {
111+
color: var(--landing-text-primary, #fafafa);
112+
border-color: var(--landing-primary, #3b82f6);
113+
background: var(--landing-primary-subtle, rgba(59, 130, 246, 0.1));
114+
}
115+
116+
.copy-button:focus-visible {
117+
outline: 2px solid var(--landing-primary, #3b82f6);
118+
outline-offset: 2px;
119+
}
120+
121+
.copy-button.copied {
122+
color: #22c55e;
123+
border-color: rgba(34, 197, 94, 0.4);
124+
background: rgba(34, 197, 94, 0.1);
125+
}
126+
127+
.copy-button.icon-only {
128+
padding: 0.375rem;
129+
}
130+
131+
.icon {
132+
width: 0.875rem;
133+
height: 0.875rem;
134+
flex-shrink: 0;
135+
}
136+
137+
.label {
138+
font-weight: 600;
139+
letter-spacing: -0.01em;
140+
}
141+
142+
.sr-only {
143+
position: absolute;
144+
width: 1px;
145+
height: 1px;
146+
padding: 0;
147+
margin: -1px;
148+
overflow: hidden;
149+
clip: rect(0, 0, 0, 0);
150+
white-space: nowrap;
151+
border: 0;
152+
}
153+
</style>

0 commit comments

Comments
 (0)