Skip to content

Commit 3cd2f8f

Browse files
Merge pull request steam-bell-92#1273 from kavin553/feature-code-copy-snoppets-1215
feat : add copy-to-clipboard support for code snippets
2 parents 4fd9742 + 7926b7f commit 3cd2f8f

5 files changed

Lines changed: 735 additions & 0 deletions

File tree

web-app/css/styles.css

Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6489,3 +6489,272 @@ body.sidebar-collapsed .sidebar-dock .sidebar-footer button {
64896489
pointer-events: auto;
64906490
}
64916491
}
6492+
6493+
/* ═══════════════════════════════════════════════════════════════
6494+
COPY BUTTON COMPONENT — Issue #1215
6495+
Copy-to-Clipboard for Code Snippets
6496+
═══════════════════════════════════════════════════════════════ */
6497+
6498+
/**
6499+
* Code Block Wrapper
6500+
* Creates relative positioning context for absolute button placement
6501+
*/
6502+
.code-block-wrapper {
6503+
position: relative !important;
6504+
display: block;
6505+
}
6506+
6507+
/**
6508+
* Copy Button
6509+
* Reusable, accessible button for copying code to clipboard
6510+
*
6511+
* Features:
6512+
* • Positioned absolutely over code block (top-right)
6513+
* • Keyboard accessible (Tab, Enter, Space)
6514+
* • Screen reader friendly (aria-label)
6515+
* • Mobile & desktop optimized
6516+
* • Theme aware (dark/light mode)
6517+
* • Visual feedback states (default, hover, active, success, error)
6518+
*/
6519+
.copy-button {
6520+
/* Layout & Positioning */
6521+
position: absolute;
6522+
top: 8px;
6523+
right: 8px;
6524+
z-index: 10;
6525+
6526+
/* Dimensions */
6527+
min-width: 60px;
6528+
padding: 6px 12px;
6529+
height: 32px;
6530+
6531+
/* Typography */
6532+
font-family: var(--font-sans);
6533+
font-size: 0.75rem;
6534+
font-weight: 600;
6535+
letter-spacing: 0.5px;
6536+
text-transform: uppercase;
6537+
line-height: 1;
6538+
white-space: nowrap;
6539+
6540+
/* Appearance */
6541+
border: 1px solid var(--border-accent);
6542+
border-radius: 8px;
6543+
background: rgba(34, 197, 94, 0.08);
6544+
color: var(--accent);
6545+
6546+
/* Interaction */
6547+
cursor: pointer;
6548+
transition:
6549+
all var(--duration-fast) ease,
6550+
box-shadow 150ms ease;
6551+
6552+
/* Reset default button styles */
6553+
margin: 0;
6554+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
6555+
6556+
/* Accessibility */
6557+
outline: 2px solid transparent;
6558+
outline-offset: 2px;
6559+
6560+
/* Prevent selection during copy action */
6561+
-webkit-user-select: none;
6562+
user-select: none;
6563+
}
6564+
6565+
/**
6566+
* Copy Button — Hover State
6567+
* Elevated appearance with enhanced visibility
6568+
*/
6569+
.copy-button:hover {
6570+
background: rgba(34, 197, 94, 0.15);
6571+
border-color: var(--accent);
6572+
box-shadow: 0 4px 12px rgba(34, 197, 94, 0.2);
6573+
transform: translateY(-1px);
6574+
}
6575+
6576+
/**
6577+
* Copy Button — Focus State
6578+
* High contrast focus ring for keyboard navigation
6579+
* Critical for accessibility (WCAG AA compliant)
6580+
*/
6581+
.copy-button:focus {
6582+
outline-color: var(--accent);
6583+
outline-width: 2px;
6584+
}
6585+
6586+
.copy-button:focus-visible {
6587+
outline-color: var(--accent);
6588+
outline-width: 2px;
6589+
outline-offset: 2px;
6590+
}
6591+
6592+
/**
6593+
* Copy Button — Active State
6594+
* Pressed down appearance when clicked
6595+
*/
6596+
.copy-button:active {
6597+
transform: translateY(0);
6598+
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
6599+
}
6600+
6601+
/**
6602+
* Copy Button — Success State (After Copy)
6603+
* "Copied!" text with success color and brief animation
6604+
*/
6605+
.copy-button.copy-success {
6606+
background: rgba(16, 185, 129, 0.12);
6607+
border-color: var(--success);
6608+
color: var(--success);
6609+
box-shadow: 0 4px 12px rgba(16, 185, 129, 0.15);
6610+
animation: copy-pulse 400ms ease-out;
6611+
}
6612+
6613+
/**
6614+
* Copy Button — Error State (Copy Failed)
6615+
* Brief error feedback if clipboard access fails
6616+
*/
6617+
.copy-button.copy-error {
6618+
background: rgba(239, 68, 68, 0.12);
6619+
border-color: var(--danger);
6620+
color: var(--danger);
6621+
box-shadow: 0 4px 12px rgba(239, 68, 68, 0.15);
6622+
}
6623+
6624+
/**
6625+
* Pulse Animation
6626+
* Gentle scale-up feedback when copy succeeds
6627+
* Helps draw user attention without being jarring
6628+
*/
6629+
@keyframes copy-pulse {
6630+
0% {
6631+
transform: scale(1);
6632+
}
6633+
50% {
6634+
transform: scale(1.05);
6635+
}
6636+
100% {
6637+
transform: scale(1);
6638+
}
6639+
}
6640+
6641+
/**
6642+
* Light Mode Adjustments
6643+
* Ensures button is visible and accessible in light theme
6644+
*/
6645+
[data-theme="light"] .copy-button {
6646+
background: rgba(34, 197, 94, 0.1);
6647+
border-color: rgba(34, 197, 94, 0.4);
6648+
color: #059669;
6649+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
6650+
}
6651+
6652+
[data-theme="light"] .copy-button:hover {
6653+
background: rgba(34, 197, 94, 0.15);
6654+
border-color: #059669;
6655+
box-shadow: 0 4px 12px rgba(34, 197, 94, 0.15);
6656+
}
6657+
6658+
[data-theme="light"] .copy-button.copy-success {
6659+
background: rgba(16, 185, 129, 0.1);
6660+
border-color: #0d9488;
6661+
color: #0d9488;
6662+
box-shadow: 0 4px 12px rgba(16, 185, 129, 0.1);
6663+
}
6664+
6665+
[data-theme="light"] .copy-button.copy-error {
6666+
background: rgba(239, 68, 68, 0.1);
6667+
border-color: #dc2626;
6668+
color: #dc2626;
6669+
box-shadow: 0 4px 12px rgba(239, 68, 68, 0.1);
6670+
}
6671+
6672+
/**
6673+
* Mobile Optimizations
6674+
* Larger touch target for mobile devices
6675+
* Prevents accidental clicks
6676+
*/
6677+
@media (max-width: 768px) {
6678+
.copy-button {
6679+
min-width: 50px;
6680+
padding: 8px 12px;
6681+
height: 36px;
6682+
font-size: 0.7rem;
6683+
top: 6px;
6684+
right: 6px;
6685+
}
6686+
6687+
/* Ensure adequate space around button for touch */
6688+
.copy-button::before {
6689+
content: "";
6690+
position: absolute;
6691+
inset: -8px;
6692+
pointer-events: none;
6693+
}
6694+
}
6695+
6696+
/**
6697+
* High Contrast Mode Support
6698+
* For users with vision impairments
6699+
* Uses forced colors to ensure readability
6700+
*/
6701+
@media (prefers-contrast: more) {
6702+
.copy-button {
6703+
border-width: 2px;
6704+
font-weight: 700;
6705+
}
6706+
6707+
.copy-button:focus,
6708+
.copy-button:focus-visible {
6709+
outline-width: 3px;
6710+
outline-offset: 3px;
6711+
}
6712+
}
6713+
6714+
/**
6715+
* Reduced Motion Support
6716+
* Respects user's motion preferences
6717+
* Disables animations for users with vestibular disorders
6718+
*/
6719+
@media (prefers-reduced-motion: reduce) {
6720+
.copy-button,
6721+
.copy-button:hover,
6722+
.copy-button:active,
6723+
.copy-button.copy-success,
6724+
.copy-button.copy-error {
6725+
transition: none;
6726+
animation: none;
6727+
transform: none !important;
6728+
}
6729+
6730+
@keyframes copy-pulse {
6731+
0%, 100% {
6732+
transform: none;
6733+
}
6734+
50% {
6735+
transform: none;
6736+
}
6737+
}
6738+
}
6739+
6740+
/* ═══════════════════════════════════════════════════════════════
6741+
PLAYGROUND COPY BUTTON STATES — Issue #1215
6742+
Visual feedback for editor code copy button
6743+
═══════════════════════════════════════════════════════════════ */
6744+
6745+
/**
6746+
* Copy Button in Playground
6747+
* Shows success/error feedback with color changes and icon updates
6748+
*/
6749+
.btn-panel-action.copy-success {
6750+
background: rgba(16, 185, 129, 0.12);
6751+
border-color: var(--success);
6752+
color: var(--success);
6753+
animation: copy-pulse 400ms ease-out;
6754+
}
6755+
6756+
.btn-panel-action.copy-error {
6757+
background: rgba(239, 68, 68, 0.12);
6758+
border-color: var(--danger);
6759+
color: var(--danger);
6760+
}

web-app/index.html

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -599,6 +599,15 @@ <h2 class="playground-title">🐍 Python Playground</h2>
599599
>
600600
<i aria-hidden="true" class="fas fa-lightbulb"></i> Example
601601
</button>
602+
<button
603+
class="btn-panel-action"
604+
id="copyEditorCode"
605+
title="Copy editor code to clipboard"
606+
type="button"
607+
aria-label="Copy code to clipboard"
608+
>
609+
<i aria-hidden="true" class="fas fa-copy"></i> Copy
610+
</button>
602611
<button
603612
class="btn-panel-action"
604613
id="clearEditor"

web-app/js/main.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
═══════════════════════════════════════════════════════════════ */
44

55
import { updateProjectVisibility } from "./modules/utils.js";
6+
import CopyButton from "./modules/copyButton.js";
67

78
const html = document.documentElement;
89
const themeToggle = document.getElementById('themeToggle');

0 commit comments

Comments
 (0)