Skip to content

Commit 4d708ed

Browse files
authored
chore: update design/functionality of copying highlighted code snippets (#7570)
* chore: design updates to copy code functionality * add hover * add hover sibling style * updates to css highlight colors * darkmode, cleanup * update file name * use space token * fix css transition * cleanup * Fix some dark mode interaction colors * add icon
1 parent 73b31e3 commit 4d708ed

File tree

7 files changed

+185
-89
lines changed

7 files changed

+185
-89
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { Icon } from '@aws-amplify/ui-react';
2+
3+
export const IconClipboard = ({ ...rest }) => {
4+
return (
5+
<Icon
6+
aria-hidden="true"
7+
{...rest}
8+
viewBox={{
9+
minX: 0,
10+
minY: 0,
11+
width: 24,
12+
height: 24
13+
}}
14+
>
15+
<path
16+
d="M18.75 3H15C15 1.34531 13.6547 0 12 0C10.3453 0 9 1.34531 9 3H5.25C4.00781 3 3 4.00781 3 5.25V21.75C3 22.9922 4.00781 24 5.25 24H18.75C19.9922 24 21 22.9922 21 21.75V5.25C21 4.00781 19.9922 3 18.75 3ZM12 1.875C12.6234 1.875 13.125 2.37656 13.125 3C13.125 3.62344 12.6234 4.125 12 4.125C11.3766 4.125 10.875 3.62344 10.875 3C10.875 2.37656 11.3766 1.875 12 1.875ZM18.75 21.4688C18.75 21.6234 18.6234 21.75 18.4688 21.75H5.53125C5.37656 21.75 5.25 21.6234 5.25 21.4688V5.53125C5.25 5.37656 5.37656 5.25 5.53125 5.25H7.5V6.9375C7.5 7.24687 7.75313 7.5 8.0625 7.5H15.9375C16.2469 7.5 16.5 7.24687 16.5 6.9375V5.25H18.4688C18.6234 5.25 18.75 5.37656 18.75 5.53125V21.4688Z"
17+
fill="currentColor"
18+
/>
19+
</Icon>
20+
);
21+
};

src/components/Icons/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export { IconAWS } from './IconAWS';
55
export { IconCheck } from './IconCheck';
66
export { IconCheckCircle } from './IconCheckCircle';
77
export { IconChevron } from './IconChevron';
8+
export { IconClipboard } from './IconClipboard';
89
export { IconDark } from './IconDark';
910
export { IconDiscord } from './IconDiscord';
1011
export { IconDoubleChevron } from './IconDoubleChevron';

src/components/MDXComponents/MDXCopyCodeButton.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { CopyToClipboard } from 'react-copy-to-clipboard';
33
import { Button, VisuallyHidden } from '@aws-amplify/ui-react';
44
import { trackCopyClicks } from '@/utils/track';
55
import { prepareCopyText } from './utils/copy-code';
6-
6+
import { IconClipboard } from '@/components/Icons';
77
interface MDXCopyCodeButtonProps {
88
codeId: string;
99
codeString: string;
@@ -38,7 +38,7 @@ export const MDXCopyCodeButton = ({
3838
testId={testId}
3939
aria-describedby={title ? undefined : codeId}
4040
>
41-
{copied ? 'Copied!' : 'Copy'}
41+
<IconClipboard /> {copied ? 'Copied!' : 'Copy'}
4242
<VisuallyHidden>
4343
{` `}
4444
{title} code example
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { useState, useId } from 'react';
2+
import { CopyToClipboard } from 'react-copy-to-clipboard';
3+
import { Button, VisuallyHidden } from '@aws-amplify/ui-react';
4+
import { trackCopyClicks } from '@/utils/track';
5+
import { prepareCopyText } from './utils/copy-code';
6+
import { IconClipboard } from '@/components/Icons';
7+
interface MDXHighlightedCodeProps {
8+
codeString: string;
9+
testId?: string;
10+
children?: React.ReactNode;
11+
}
12+
13+
export const MDXHighlightedCode = ({
14+
codeString,
15+
children
16+
}: MDXHighlightedCodeProps) => {
17+
const [copied, setCopied] = useState(false);
18+
19+
const copyText = prepareCopyText(codeString);
20+
const highlightCodeId = useId();
21+
22+
const copy = () => {
23+
trackCopyClicks(copyText);
24+
setCopied(true);
25+
setTimeout(() => {
26+
setCopied(false);
27+
}, 2000);
28+
};
29+
return (
30+
<>
31+
<CopyToClipboard text={copyText} onCopy={copy}>
32+
<Button
33+
aria-describedby={highlightCodeId}
34+
size="small"
35+
className="highlight-copy-button"
36+
>
37+
<IconClipboard />
38+
{copied ? 'Copied' : 'Copy'}
39+
40+
<VisuallyHidden>
41+
{` `}
42+
highlighted code example
43+
</VisuallyHidden>
44+
</Button>
45+
</CopyToClipboard>
46+
<CopyToClipboard text={copyText} onCopy={copy}>
47+
<div className="highlight-code" id={highlightCodeId}>
48+
{children}
49+
</div>
50+
</CopyToClipboard>
51+
</>
52+
);
53+
};

src/components/MDXComponents/MDXHighlightedCopyCodeButton.tsx

Lines changed: 0 additions & 46 deletions
This file was deleted.

src/components/MDXComponents/TokenList.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { Token } from 'prism-react-renderer';
22
import type { TokenListProps } from './types';
3-
import { MDXHighlightedCopyCodeButton } from './MDXHighlightedCopyCodeButton';
3+
import { MDXHighlightedCode } from './MDXHighlightedCode';
44
import classNames from 'classnames';
55

66
type ProcessedToken = {
@@ -159,8 +159,7 @@ export const TokenList = ({
159159
.join('\n');
160160

161161
return (
162-
<MDXHighlightedCopyCodeButton
163-
codeId={`highlighted:${i}`}
162+
<MDXHighlightedCode
164163
key={`highlighted:${i}`}
165164
codeString={highlightedCodeString}
166165
>
@@ -172,7 +171,7 @@ export const TokenList = ({
172171
showLineNumbers
173172
);
174173
})}
175-
</MDXHighlightedCopyCodeButton>
174+
</MDXHighlightedCode>
176175
);
177176
}
178177
});

src/styles/code.scss

Lines changed: 105 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ code:not([class]) {
3737
}
3838

3939
.pre-wrapper {
40-
padding: 0 var(--amplify-space-large) 0 var(--amplify-space-xs);
40+
padding: 0 var(--amplify-space-xs);
4141
}
4242

4343
.pre-header {
@@ -70,13 +70,16 @@ code:not([class]) {
7070
-webkit-text-size-adjust: 100%;
7171

7272
&:focus-visible {
73-
outline: none;
74-
box-shadow: 0 0 0 2px var(--amplify-colors-white) inset;
73+
outline: 2px solid #fff;
74+
outline-offset: 2px;
7575
}
7676
}
7777

7878
.pre-code {
7979
flex: 1;
80+
display: flex;
81+
flex-direction: column;
82+
--highlight-code-hover-background-color: hsl(211, 22%, 19%);
8083
}
8184

8285
.token-line {
@@ -92,7 +95,15 @@ code:not([class]) {
9295
}
9396

9497
.line-highlight {
98+
&:first-child {
99+
padding-top: var(--amplify-space-xxs);
100+
}
101+
&:last-child {
102+
padding-bottom: var(--amplify-space-xxs);
103+
}
95104
&:before {
105+
transition: background-color
106+
var(--amplify-components-button-transition-duration) ease;
96107
content: '';
97108
position: absolute;
98109
left: 0;
@@ -138,45 +149,101 @@ code:not([class]) {
138149
width: 1.8rem;
139150
}
140151

141-
.highlight-copy-block {
142-
all: unset;
143-
width: 100%;
144-
cursor: pointer;
152+
.highlight-copy-button {
153+
background-color: var(--amplify-colors-neutral-90);
154+
margin-top: var(--amplify-space-xxs);
155+
border-color: transparent;
156+
color: #fff;
157+
align-self: flex-start;
158+
border-end-end-radius: 0;
159+
border-end-start-radius: 0;
160+
margin-inline-start: var(--amplify-space-xxl);
145161
position: relative;
146-
}
147-
148-
.highlight-copy-block-hint {
149-
position: absolute;
150-
top: 0;
151-
right: 1.8rem;
152-
color: white;
153-
}
154-
155-
.highlight-copy-block .highlight-c4py-block-hint {
156-
display: none;
157-
}
158-
159-
.highlight-copy-block:focus .highlight-copy-block-hint {
160-
display: block;
161-
}
162-
163-
.highlight-copy-block:hover .highlight-copy-block-hint {
164-
display: block;
165-
}
166-
167-
.highlight-copy-block:focus .line-highlight::before {
168-
background-color: var(--amplify-colors-primary-80);
169-
162+
user-select: none;
163+
gap: var(--amplify-space-xxs);
164+
&:focus {
165+
box-shadow: none;
166+
}
167+
&:focus-visible {
168+
outline: 2px solid #fff;
169+
outline-offset: -2px;
170+
&:after {
171+
content: '';
172+
position: absolute;
173+
height: 2px;
174+
left: 1px;
175+
right: 1px;
176+
bottom: -1px;
177+
background-color: var(--amplify-colors-neutral-90);
178+
z-index: 2;
179+
}
180+
& + .highlight-code {
181+
outline: 2px solid #fff;
182+
.line-highlight:after {
183+
background-color: #fff;
184+
}
185+
}
186+
}
187+
&:hover {
188+
background-color: var(--highlight-code-hover-background-color);
189+
&:after {
190+
background-color: var(--highlight-code-hover-background-color);
191+
}
192+
& + .highlight-code {
193+
.line-highlight:before {
194+
background-color: var(--highlight-code-hover-background-color);
195+
}
196+
}
197+
}
170198
@include darkMode {
171-
background-color: var(--amplify-colors-neutral-40);
199+
background-color: var(--amplify-colors-neutral-20);
200+
&:focus-visible {
201+
&:after {
202+
background-color: var(--amplify-colors-neutral-20);
203+
}
204+
}
205+
&:hover {
206+
background-color: var(--highlight-code-hover-background-color);
207+
&:after {
208+
background-color: var(--highlight-code-hover-background-color);
209+
}
210+
& + .highlight-code {
211+
.line-highlight:before {
212+
background-color: var(--highlight-code-hover-background-color);
213+
}
214+
}
215+
}
172216
}
173217
}
174218

175-
.highlight-copy-block:hover .line-highlight::before {
176-
background-color: var(--amplify-colors-primary-90);
219+
/* This :has selector allows us to style the .highlight-copy-button
220+
that is preceding the .highlight-code block so that interaction between
221+
them looks "connected", i.e. the hover style on one triggers the
222+
hover style on the other */
223+
.highlight-copy-button:has(+ .highlight-code:hover) {
224+
background-color: var(--highlight-code-hover-background-color);
225+
&:focus-visible {
226+
&:after {
227+
height: 4px;
228+
bottom: -3px;
229+
background-color: var(--highlight-code-hover-background-color);
230+
}
231+
}
232+
&:hover {
233+
&:after {
234+
background-color: var(--highlight-code-hover-background-color);
235+
}
236+
}
237+
}
177238

178-
@include darkMode {
179-
background-color: var(--amplify-colors-primary-10);
239+
.highlight-code {
240+
position: relative;
241+
margin-bottom: var(--amplify-space-xxs);
242+
&:hover {
243+
cursor: pointer;
244+
.line-highlight:before {
245+
background-color: var(--highlight-code-hover-background-color);
246+
}
180247
}
181248
}
182249

@@ -188,6 +255,7 @@ code:not([class]) {
188255
padding-block: var(--amplify-space-xxxs);
189256
color: var(--amplify-colors-white);
190257
border-radius: var(--amplify-radii-small);
258+
gap: var(--amplify-space-xxs);
191259
&:hover {
192260
color: var(--amplify-colors-white);
193261
background-color: var(--code-copy-hover-background-color);
@@ -202,4 +270,4 @@ code:not([class]) {
202270
--code-copy-focus-background-color: var(--amplify-colors-neutral-20);
203271
--code-copy-focus-box-shadow: var(--amplify-colors-neutral-100);
204272
}
205-
}
273+
}

0 commit comments

Comments
 (0)