Skip to content

Commit d71a67d

Browse files
committed
feat(styleguide): add one-click copy for design tokens
1 parent 24829f0 commit d71a67d

2 files changed

Lines changed: 66 additions & 12 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ All notable changes to this project will be documented in this file. We will log
1313
- **Motion & Animation System**: Implemented a dedicated `basic-motion` plugin with global duration and easing tokens, integrated across all components for smooth, consistent transitions.
1414
- **Custom HTML Sandbox**: Added a new sandbox plugin allowing users to test their current theme against custom markup with real-time feedback.
1515
- **Theme Presets (Personas)**: Introduced a library of "One-Click Personas" (Midnight Pro, Cyberpunk, Corporate Clean, Minimalist) for rapid theme experimentation.
16+
- **Dev Handoff: Copy to Clipboard**: Added one-click copy functionality to the Style Guide, allowing users to instantly copy design tokens as CSS variables.
1617

1718
### Changed
1819

src/plugins/basic-styleguide/index.ts

Lines changed: 65 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ export const styleguideControlModule: ControlModule = {
1313
<div style="padding: 1rem; background: var(--surface-bg); border-radius: 8px; border: 1px solid rgba(128,128,128,0.1); font-size: 11px; opacity: 0.8;">
1414
This view is auto-generated based on your current theme configuration. Use it to hand off specs to developers.
1515
</div>
16+
<div style="margin-top: 1rem; padding: 0.75rem; background: var(--color-primary-500); color: var(--on-primary); border-radius: 8px; font-size: 10px; font-weight: 700; display: flex; align-items: center; gap: 8px;">
17+
<span>💡 Tip: Click any token name to copy its CSS variable.</span>
18+
</div>
1619
`;
1720
}
1821
};
@@ -34,7 +37,7 @@ export const styleguidePreviewModule: PreviewModule = {
3437
<div style="width: 32px; height: 32px; background: var(--${prefix}-${step}); border-radius: 4px; border: 1px solid rgba(128,128,128,0.2);"></div>
3538
<div style="display: flex; flex-direction: column;">
3639
<span style="font-size: 11px; font-weight: 700;">${step}</span>
37-
<code style="font-size: 9px; opacity: 0.6;">--${prefix}-${step}</code>
40+
<code class="token-code" onclick="copyToken('--${prefix}-${step}')">--${prefix}-${step}</code>
3841
</div>
3942
</div>
4043
`).join('')}
@@ -43,6 +46,56 @@ export const styleguidePreviewModule: PreviewModule = {
4346
`;
4447

4548
return `
49+
<style>
50+
.token-code {
51+
font-size: 9px;
52+
opacity: 0.6;
53+
cursor: pointer;
54+
transition: all 0.2s ease;
55+
padding: 2px 4px;
56+
border-radius: 4px;
57+
}
58+
.token-code:hover {
59+
opacity: 1;
60+
background: var(--color-primary-500);
61+
color: var(--on-primary);
62+
}
63+
.token-code:active {
64+
transform: scale(0.95);
65+
}
66+
#copy-toast {
67+
position: fixed;
68+
bottom: 20px;
69+
left: 50%;
70+
transform: translateX(-50%) translateY(100px);
71+
background: #0f172a;
72+
color: white;
73+
padding: 8px 16px;
74+
border-radius: 20px;
75+
font-size: 12px;
76+
font-weight: 600;
77+
transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
78+
z-index: 9999;
79+
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
80+
}
81+
#copy-toast.show {
82+
transform: translateX(-50%) translateY(0);
83+
}
84+
</style>
85+
86+
<script>
87+
window.copyToken = (text) => {
88+
navigator.clipboard.writeText(text).then(() => {
89+
const toast = document.getElementById('copy-toast');
90+
toast.textContent = 'Copied: ' + text;
91+
toast.classList.add('show');
92+
setTimeout(() => toast.classList.remove('show'), 2000);
93+
});
94+
};
95+
</script>
96+
97+
<div id="copy-toast">Copied!</div>
98+
4699
<div style="display: flex; flex-direction: column; gap: 3rem; color: var(--on-background); font-family: var(--font-family, sans-serif);">
47100
48101
<!-- TYPOGRAPHY SPEC -->
@@ -53,7 +106,7 @@ export const styleguidePreviewModule: PreviewModule = {
53106
<div style="display: grid; grid-template-columns: 100px 1fr; align-items: baseline; gap: 2rem; padding: 1rem; background: var(--surface-card); border-radius: 8px; border: 1px solid rgba(128,128,128,0.1);">
54107
<div style="display: flex; flex-direction: column; gap: 0.25rem;">
55108
<span style="font-size: 11px; font-weight: 800; text-transform: uppercase; opacity: 0.5;">${size}</span>
56-
<code style="font-size: 10px; color: var(--color-primary-500);">--text-${size}-size</code>
109+
<code class="token-code" style="font-size: 10px;" onclick="copyToken('--text-${size}-size')">--text-${size}-size</code>
57110
</div>
58111
<div style="font-size: var(--text-${size}-size); line-height: var(--text-${size}-line-height);">
59112
The quick brown fox jumps over the lazy dog.
@@ -81,8 +134,8 @@ export const styleguidePreviewModule: PreviewModule = {
81134
<div style="width: 12px; height: 12px; border-radius: 50%; background: var(--color-${role}-500);"></div>
82135
</div>
83136
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 0.5rem;">
84-
<div style="height: 32px; background: var(--color-${role}-500); border-radius: 4px; display: flex; align-items: center; justify-content: center; font-size: 9px; font-weight: 700; color: var(--on-${role});">Solid</div>
85-
<div style="height: 32px; background: color-mix(in srgb, var(--color-${role}-500) 15%, transparent); border: 1px solid var(--color-${role}-500); border-radius: 4px; display: flex; align-items: center; justify-content: center; font-size: 9px; font-weight: 700; color: var(--color-${role}-600);">Soft</div>
137+
<div class="token-code" style="height: 32px; background: var(--color-${role}-500); border-radius: 4px; display: flex; align-items: center; justify-content: center; font-size: 9px; font-weight: 700; color: var(--on-${role});" onclick="copyToken('var(--color-${role}-500)')">Solid</div>
138+
<div class="token-code" style="height: 32px; background: color-mix(in srgb, var(--color-${role}-500) 15%, transparent); border: 1px solid var(--color-${role}-500); border-radius: 4px; display: flex; align-items: center; justify-content: center; font-size: 9px; font-weight: 700; color: var(--color-${role}-600);" onclick="copyToken('var(--color-${role}-600)')">Soft</div>
86139
</div>
87140
</div>
88141
`).join('')}
@@ -103,7 +156,7 @@ export const styleguidePreviewModule: PreviewModule = {
103156
<div style="display: flex; justify-content: space-between; align-items: center;">
104157
<div style="display: flex; flex-direction: column;">
105158
<span style="font-size: 11px; font-weight: 800;">${bp.toUpperCase()}</span>
106-
<code style="font-size: 9px; opacity: 0.5;">--breakpoint-${bp}</code>
159+
<code class="token-code" onclick="copyToken('--breakpoint-${bp}')">--breakpoint-${bp}</code>
107160
</div>
108161
<span style="font-size: 11px; font-weight: 700; color: var(--color-primary-500);">${config.layout?.breakpoints[bp as keyof typeof config.layout.breakpoints]}</span>
109162
</div>
@@ -117,14 +170,14 @@ export const styleguidePreviewModule: PreviewModule = {
117170
<div style="display: flex; justify-content: space-between; align-items: center;">
118171
<div style="display: flex; flex-direction: column;">
119172
<span style="font-size: 11px; font-weight: 800;">Container Max</span>
120-
<code style="font-size: 9px; opacity: 0.5;">--container-width</code>
173+
<code class="token-code" onclick="copyToken('--container-width')">--container-width</code>
121174
</div>
122175
<span style="font-size: 11px; font-weight: 700; color: var(--color-primary-500);">${config.layout?.container}</span>
123176
</div>
124177
<div style="display: flex; justify-content: space-between; align-items: center;">
125178
<div style="display: flex; flex-direction: column;">
126179
<span style="font-size: 11px; font-weight: 800;">Grid Gutter</span>
127-
<code style="font-size: 9px; opacity: 0.5;">--layout-gutter</code>
180+
<code class="token-code" onclick="copyToken('--layout-gutter')">--layout-gutter</code>
128181
</div>
129182
<span style="font-size: 11px; font-weight: 700; color: var(--color-primary-500);">${config.layout?.gutter}</span>
130183
</div>
@@ -145,7 +198,7 @@ export const styleguidePreviewModule: PreviewModule = {
145198
${radiusKeys.map(key => `
146199
<div style="display: flex; flex-direction: column; gap: 0.5rem; align-items: center;">
147200
<div style="width: 48px; height: 48px; background: var(--color-primary-500); border-radius: var(--radius-${key});"></div>
148-
<code style="font-size: 10px;">${key}</code>
201+
<code class="token-code" onclick="copyToken('--radius-${key}')">${key}</code>
149202
</div>
150203
`).join('')}
151204
</div>
@@ -157,7 +210,7 @@ export const styleguidePreviewModule: PreviewModule = {
157210
<div style="display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 1rem;">
158211
${shadowKeys.map(key => `
159212
<div style="height: 60px; background: var(--surface-bg); border-radius: 8px; box-shadow: var(--shadow-${key}); display: flex; align-items: center; justify-content: center; border: 1px solid rgba(128,128,128,0.05);">
160-
<code style="font-size: 10px; font-weight: 800;">${key.toUpperCase()}</code>
213+
<code class="token-code" style="font-weight: 800;" onclick="copyToken('--shadow-${key}')">${key.toUpperCase()}</code>
161214
</div>
162215
`).join('')}
163216
</div>
@@ -172,7 +225,7 @@ export const styleguidePreviewModule: PreviewModule = {
172225
<div style="width: var(--space-${key}); height: 8px; background: var(--color-primary-500); border-radius: 2px;"></div>
173226
<div style="display: flex; justify-content: space-between; align-items: center;">
174227
<span style="font-size: 10px; font-weight: 700;">${key}</span>
175-
<code style="font-size: 9px; opacity: 0.6;">var(--space-${key})</code>
228+
<code class="token-code" onclick="copyToken('--space-${key}')">var(--space-${key})</code>
176229
</div>
177230
</div>
178231
`).join('')}
@@ -192,7 +245,7 @@ export const styleguidePreviewModule: PreviewModule = {
192245
<div style="display: flex; justify-content: space-between; align-items: center;">
193246
<div style="display: flex; flex-direction: column;">
194247
<span style="font-size: 11px; font-weight: 800;">${d.toUpperCase()}</span>
195-
<code style="font-size: 9px; opacity: 0.5;">--duration-${d}</code>
248+
<code class="token-code" onclick="copyToken('--duration-${d}')">--duration-${d}</code>
196249
</div>
197250
<span style="font-size: 11px; font-weight: 700; color: var(--color-primary-500);">${config.motion?.durations[d as keyof typeof config.motion.durations]}ms</span>
198251
</div>
@@ -206,7 +259,7 @@ export const styleguidePreviewModule: PreviewModule = {
206259
<div style="display: flex; justify-content: space-between; align-items: center;">
207260
<div style="display: flex; flex-direction: column;">
208261
<span style="font-size: 11px; font-weight: 800;">${e.toUpperCase()}</span>
209-
<code style="font-size: 9px; opacity: 0.5;">--ease-${e.replace(/[A-Z]/, m => '-' + m.toLowerCase())}</code>
262+
<code class="token-code" onclick="copyToken('--ease-${e.replace(/[A-Z]/, m => '-' + m.toLowerCase())}')">--ease-${e.replace(/[A-Z]/, m => '-' + m.toLowerCase())}</code>
210263
</div>
211264
</div>
212265
`).join('')}

0 commit comments

Comments
 (0)