Skip to content

Commit 42d317f

Browse files
authored
Improve Twitter theme (#99)
* Updated images to take the proper height * Added new tweet video buttons * Handle the new verified icons * Updated theme * Use const * Added changeset * Added missing extensions * Added package export for the icons
1 parent c64f717 commit 42d317f

15 files changed

+295
-75
lines changed

.changeset/brave-geese-clap.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'react-tweet': patch
3+
---
4+
5+
Updated Twitter theme

packages/react-tweet/src/api/types/user.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ export interface TweetUser {
22
id_str: string
33
name: string
44
profile_image_url_https: string
5+
profile_image_shape: 'Circle' | 'Square'
56
screen_name: string
67
verified: boolean
7-
verified_type: string
8+
verified_type?: 'Business' | 'Government'
89
is_blue_verified: boolean
910
}

packages/react-tweet/src/twitter-theme/components.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export * from './types.js'
2+
export * from './icons/index.js'
23
export * from './embedded-tweet.js'
34
export * from './tweet-actions-copy.js'
45
export * from './tweet-actions.js'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
.verified {
2+
margin-left: 0.125rem;
3+
max-width: 20px;
4+
max-height: 20px;
5+
height: 1.25em;
6+
fill: currentColor;
7+
user-select: none;
8+
vertical-align: text-bottom;
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export * from './verified.js'
2+
export * from './verified-business.js'
3+
export * from './verified-government.js'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import s from './icons.module.css'
2+
3+
export const VerifiedBusiness = () => (
4+
<svg
5+
viewBox="0 0 22 22"
6+
aria-label="Verified account"
7+
role="img"
8+
className={s.verified}
9+
>
10+
<g>
11+
<linearGradient
12+
gradientUnits="userSpaceOnUse"
13+
id="0-a"
14+
x1="4.411"
15+
x2="18.083"
16+
y1="2.495"
17+
y2="21.508"
18+
>
19+
<stop offset="0" stopColor="#f4e72a"></stop>
20+
<stop offset=".539" stopColor="#cd8105"></stop>
21+
<stop offset=".68" stopColor="#cb7b00"></stop>
22+
<stop offset="1" stopColor="#f4ec26"></stop>
23+
<stop offset="1" stopColor="#f4e72a"></stop>
24+
</linearGradient>
25+
<linearGradient
26+
gradientUnits="userSpaceOnUse"
27+
id="0-b"
28+
x1="5.355"
29+
x2="16.361"
30+
y1="3.395"
31+
y2="19.133"
32+
>
33+
<stop offset="0" stopColor="#f9e87f"></stop>
34+
<stop offset=".406" stopColor="#e2b719"></stop>
35+
<stop offset=".989" stopColor="#e2b719"></stop>
36+
</linearGradient>
37+
<g clipRule="evenodd" fillRule="evenodd">
38+
<path
39+
d="M13.324 3.848L11 1.6 8.676 3.848l-3.201-.453-.559 3.184L2.06 8.095 3.48 11l-1.42 2.904 2.856 1.516.559 3.184 3.201-.452L11 20.4l2.324-2.248 3.201.452.559-3.184 2.856-1.516L18.52 11l1.42-2.905-2.856-1.516-.559-3.184zm-7.09 7.575l3.428 3.428 5.683-6.206-1.347-1.247-4.4 4.795-2.072-2.072z"
40+
fill="url(#0-a)"
41+
></path>
42+
<path
43+
d="M13.101 4.533L11 2.5 8.899 4.533l-2.895-.41-.505 2.88-2.583 1.37L4.2 11l-1.284 2.627 2.583 1.37.505 2.88 2.895-.41L11 19.5l2.101-2.033 2.895.41.505-2.88 2.583-1.37L17.8 11l1.284-2.627-2.583-1.37-.505-2.88zm-6.868 6.89l3.429 3.428 5.683-6.206-1.347-1.247-4.4 4.795-2.072-2.072z"
44+
fill="url(#0-b)"
45+
></path>
46+
<path
47+
d="M6.233 11.423l3.429 3.428 5.65-6.17.038-.033-.005 1.398-5.683 6.206-3.429-3.429-.003-1.405.005.003z"
48+
fill="#d18800"
49+
></path>
50+
</g>
51+
</g>
52+
</svg>
53+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import s from './icons.module.css'
2+
3+
export const VerifiedGovernment = () => (
4+
<svg
5+
viewBox="0 0 22 22"
6+
aria-label="Verified account"
7+
role="img"
8+
className={s.verified}
9+
>
10+
<g>
11+
<path
12+
clipRule="evenodd"
13+
d="M12.05 2.056c-.568-.608-1.532-.608-2.1 0l-1.393 1.49c-.284.303-.685.47-1.1.455L5.42 3.932c-.832-.028-1.514.654-1.486 1.486l.069 2.039c.014.415-.152.816-.456 1.1l-1.49 1.392c-.608.568-.608 1.533 0 2.101l1.49 1.393c.304.284.47.684.456 1.1l-.07 2.038c-.027.832.655 1.514 1.487 1.486l2.038-.069c.415-.014.816.152 1.1.455l1.392 1.49c.569.609 1.533.609 2.102 0l1.393-1.49c.283-.303.684-.47 1.099-.455l2.038.069c.832.028 1.515-.654 1.486-1.486L18 14.542c-.015-.415.152-.815.455-1.099l1.49-1.393c.608-.568.608-1.533 0-2.101l-1.49-1.393c-.303-.283-.47-.684-.455-1.1l.068-2.038c.029-.832-.654-1.514-1.486-1.486l-2.038.07c-.415.013-.816-.153-1.1-.456zm-5.817 9.367l3.429 3.428 5.683-6.206-1.347-1.247-4.4 4.795-2.072-2.072z"
14+
fillRule="evenodd"
15+
></path>
16+
</g>
17+
</svg>
18+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import s from './icons.module.css'
2+
3+
export const Verified = () => (
4+
<svg
5+
viewBox="0 0 24 24"
6+
aria-label="Verified account"
7+
role="img"
8+
className={s.verified}
9+
>
10+
<g>
11+
<path d="M22.25 12c0-1.43-.88-2.67-2.19-3.34.46-1.39.2-2.9-.81-3.91s-2.52-1.27-3.91-.81c-.66-1.31-1.91-2.19-3.34-2.19s-2.67.88-3.33 2.19c-1.4-.46-2.91-.2-3.92.81s-1.26 2.52-.8 3.91c-1.31.67-2.2 1.91-2.2 3.34s.89 2.67 2.2 3.34c-.46 1.39-.21 2.9.8 3.91s2.52 1.26 3.91.81c.67 1.31 1.91 2.19 3.34 2.19s2.68-.88 3.34-2.19c1.39.45 2.9.2 3.91-.81s1.27-2.52.81-3.91c1.31-.67 2.19-1.91 2.19-3.34zm-11.71 4.2L6.8 12.46l1.41-1.42 2.26 2.26 4.8-5.23 1.47 1.36-6.2 6.77z"></path>
12+
</g>
13+
</svg>
14+
)

packages/react-tweet/src/twitter-theme/theme.css

+3
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
--tweet-bg-color: #fff;
5454
--tweet-bg-color-hover: rgb(247, 249, 249);
5555
--tweet-color-blue-primary: rgb(29, 155, 240);
56+
--tweet-color-blue-primary-hover: rgb(26, 140, 216);
5657
--tweet-color-blue-secondary: rgb(0, 111, 214);
5758
--tweet-color-blue-secondary-hover: rgba(0, 111, 214, 0.1);
5859
--tweet-color-red-primary: rgb(249, 24, 128);
@@ -80,6 +81,7 @@
8081
--tweet-bg-color: rgb(21, 32, 43);
8182
--tweet-bg-color-hover: rgb(30, 39, 50);
8283
--tweet-color-blue-primary: rgb(29, 155, 240);
84+
--tweet-color-blue-primary-hover: rgb(26, 140, 216);
8385
--tweet-color-blue-secondary: rgb(107, 201, 251);
8486
--tweet-color-blue-secondary-hover: rgba(107, 201, 251, 0.1);
8587
--tweet-color-red-primary: rgb(249, 24, 128);
@@ -108,6 +110,7 @@
108110
--tweet-bg-color: rgb(21, 32, 43);
109111
--tweet-bg-color-hover: rgb(30, 39, 50);
110112
--tweet-color-blue-primary: rgb(29, 155, 240);
113+
--tweet-color-blue-primary-hover: rgb(26, 140, 216);
111114
--tweet-color-blue-secondary: rgb(107, 201, 251);
112115
--tweet-color-blue-secondary-hover: rgba(107, 201, 251, 0.1);
113116
--tweet-color-red-primary: rgb(249, 24, 128);

packages/react-tweet/src/twitter-theme/tweet-header.module.css

+7-9
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@
2020
overflow: hidden;
2121
border-radius: 9999px;
2222
}
23+
.avatarSquare {
24+
border-radius: 4px;
25+
}
2326
.avatarShadow {
2427
height: 100%;
2528
width: 100%;
@@ -56,21 +59,16 @@
5659
overflow: hidden;
5760
white-space: nowrap;
5861
}
59-
.authorVerifiedIcon {
60-
margin-left: 0.125rem;
61-
max-width: 20px;
62-
max-height: 20px;
63-
height: 1.25em;
64-
fill: currentColor;
65-
user-select: none;
66-
vertical-align: text-bottom;
67-
}
6862
.verifiedOld {
6963
color: var(--tweet-verified-old-color);
7064
}
7165
.verifiedBlue {
7266
color: var(--tweet-verified-blue-color);
7367
}
68+
.verifiedGovernment {
69+
/* color: var(--tweet-verified-government-color); */
70+
color: rgb(130, 154, 171);
71+
}
7472
.authorMeta {
7573
display: flex;
7674
}

packages/react-tweet/src/twitter-theme/tweet-header.tsx

+39-28
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@ import clsx from 'clsx'
22
import type { EnrichedTweet } from '../utils.js'
33
import type { TwitterComponents } from './types.js'
44
import { AvatarImg } from './avatar-img.js'
5+
import {
6+
Verified,
7+
VerifiedGovernment,
8+
VerifiedBusiness,
9+
} from './icons/index.js'
510
import s from './tweet-header.module.css'
611

712
type Props = {
@@ -11,6 +16,26 @@ type Props = {
1116

1217
export const TweetHeader = ({ tweet, components }: Props) => {
1318
const Img = components?.AvatarImg ?? AvatarImg
19+
const { user } = tweet
20+
const verified = user.verified || user.is_blue_verified || user.verified_type
21+
let icon = <Verified />
22+
let iconClassName: string | null = s.verifiedBlue
23+
24+
if (verified) {
25+
if (!user.is_blue_verified) {
26+
iconClassName = s.verifiedOld
27+
}
28+
switch (user.verified_type) {
29+
case 'Government':
30+
icon = <VerifiedGovernment />
31+
iconClassName = s.verifiedGovernment
32+
break
33+
case 'Business':
34+
icon = <VerifiedBusiness />
35+
iconClassName = null
36+
break
37+
}
38+
}
1439

1540
return (
1641
<div className={s.header}>
@@ -20,10 +45,15 @@ export const TweetHeader = ({ tweet, components }: Props) => {
2045
target="_blank"
2146
rel="noopener noreferrer"
2247
>
23-
<div className={s.avatarOverflow}>
48+
<div
49+
className={clsx(
50+
s.avatarOverflow,
51+
user.profile_image_shape === 'Square' && s.avatarSquare
52+
)}
53+
>
2454
<Img
25-
src={tweet.user.profile_image_url_https}
26-
alt={tweet.user.name}
55+
src={user.profile_image_url_https}
56+
alt={user.name}
2757
width={48}
2858
height={48}
2959
/>
@@ -40,28 +70,11 @@ export const TweetHeader = ({ tweet, components }: Props) => {
4070
rel="noopener noreferrer"
4171
>
4272
<div className={s.authorLinkText}>
43-
<span title={tweet.user.name}>{tweet.user.name}</span>
73+
<span title={user.name}>{user.name}</span>
4474
</div>
45-
{tweet.user.verified ||
46-
(tweet.user.is_blue_verified && (
47-
<div
48-
className={clsx(
49-
s.authorVerified,
50-
tweet.user.is_blue_verified ? s.verifiedBlue : s.verifiedOld
51-
)}
52-
>
53-
<svg
54-
viewBox="0 0 24 24"
55-
aria-label="Verified account"
56-
role="img"
57-
className={s.authorVerifiedIcon}
58-
>
59-
<g>
60-
<path d="M22.25 12c0-1.43-.88-2.67-2.19-3.34.46-1.39.2-2.9-.81-3.91s-2.52-1.27-3.91-.81c-.66-1.31-1.91-2.19-3.34-2.19s-2.67.88-3.33 2.19c-1.4-.46-2.91-.2-3.92.81s-1.26 2.52-.8 3.91c-1.31.67-2.2 1.91-2.2 3.34s.89 2.67 2.2 3.34c-.46 1.39-.21 2.9.8 3.91s2.52 1.26 3.91.81c.67 1.31 1.91 2.19 3.34 2.19s2.68-.88 3.34-2.19c1.39.45 2.9.2 3.91-.81s1.27-2.52.81-3.91c1.31-.67 2.19-1.91 2.19-3.34zm-11.71 4.2L6.8 12.46l1.41-1.42 2.26 2.26 4.8-5.23 1.47 1.36-6.2 6.77z"></path>
61-
</g>
62-
</svg>
63-
</div>
64-
))}
75+
{verified && (
76+
<div className={clsx(s.authorVerified, iconClassName)}>{icon}</div>
77+
)}
6578
</a>
6679
<div className={s.authorMeta}>
6780
<a
@@ -70,14 +83,12 @@ export const TweetHeader = ({ tweet, components }: Props) => {
7083
target="_blank"
7184
rel="noopener noreferrer"
7285
>
73-
<span title={`@${tweet.user.screen_name}`}>
74-
@{tweet.user.screen_name}
75-
</span>
86+
<span title={`@${user.screen_name}`}>@{user.screen_name}</span>
7687
</a>
7788
<div className={s.authorFollow}>
7889
<span className={s.separator}>·</span>
7990
<a
80-
href={tweet.user.follow_url}
91+
href={user.follow_url}
8192
className={s.follow}
8293
target="_blank"
8394
rel="noopener noreferrer"

packages/react-tweet/src/twitter-theme/tweet-media-video.module.css

+44-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
1+
.anchor {
2+
display: flex;
3+
align-items: center;
4+
color: white;
5+
padding: 0 1rem;
6+
border: 1px solid transparent;
7+
border-radius: 9999px;
8+
font-weight: 700;
9+
transition: background-color 0.2s;
10+
cursor: pointer;
11+
user-select: none;
12+
outline-style: none;
13+
text-decoration: none;
14+
text-overflow: ellipsis;
15+
white-space: nowrap;
16+
}
117
.videoButton {
218
position: relative;
319
height: 67px;
@@ -14,7 +30,7 @@
1430
}
1531
.videoButton:hover,
1632
.videoButton:focus-visible {
17-
background-color: rgb(26, 140, 216);
33+
background-color: var(--tweet-color-blue-primary-hover);
1834
}
1935
.videoButtonIcon {
2036
margin-left: 3px;
@@ -25,3 +41,30 @@
2541
fill: currentColor;
2642
user-select: none;
2743
}
44+
.watchOnTwitter {
45+
position: absolute;
46+
top: 12px;
47+
right: 8px;
48+
}
49+
.watchOnTwitter > a {
50+
min-width: 2rem;
51+
min-height: 2rem;
52+
font-size: 0.875rem;
53+
line-height: 1rem;
54+
backdrop-filter: blur(4px);
55+
background-color: rgba(15, 20, 25, 0.75);
56+
}
57+
.watchOnTwitter > a:hover {
58+
background-color: rgba(39, 44, 48, 0.75);
59+
}
60+
.viewReplies {
61+
position: relative;
62+
min-height: 2rem;
63+
background-color: var(--tweet-color-blue-primary);
64+
border-color: var(--tweet-color-blue-primary);
65+
font-size: 0.9375rem;
66+
line-height: 1.25rem;
67+
}
68+
.viewReplies:hover {
69+
background-color: var(--tweet-color-blue-primary-hover);
70+
}

0 commit comments

Comments
 (0)