Skip to content
This repository was archived by the owner on Jul 31, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@
"@radix-ui/react-slot": "^1.1.2",
"@radix-ui/react-switch": "^1.1.3",
"@radix-ui/react-tooltip": "^1.1.8",
"@rive-app/react-canvas-lite": "^4.22.0",
"ai": "^4.3.15",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
Expand Down
18 changes: 18 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

118 changes: 118 additions & 0 deletions src/components/Logo/Animated.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import type { Meta, StoryObj } from '@storybook/react'
import { AnimatedLogo } from './Animated'

const meta: Meta<typeof AnimatedLogo> = {
component: AnimatedLogo,
tags: ['autodocs'],
title: 'components/Logo/Animated',
}

export default meta

type Story = StoryObj<typeof AnimatedLogo>

export const Default: Story = {}

export const DefaultWithWordmark: Story = {
args: {
variant: 'icon-with-wordmark',
},
}

export const Small: Story = {
args: {
size: 'sm',
},
}

export const Medium: Story = {
args: {
size: 'md',
},
}

export const Large: Story = {
args: {
size: 'lg',
},
}

export const ExtraLarge: Story = {
args: {
size: 'xl',
},
}

export const Wordmark: Story = {
args: {
size: 'xl',
variant: 'icon-with-wordmark',
},
}

const VariantPreview = ({
title,
children,
}: {
title: string
children: React.ReactNode
}) => (
<div className="flex min-h-20 flex-col gap-4">
<h3 className="text-muted-foreground border-b pb-2 text-sm font-medium">
{title}
</h3>
{children}
</div>
)

export const AllVariants: Story = {
render: () => (
<div className="flex max-w-2xl flex-row justify-between p-6">
<div className="flex flex-col gap-4">
<h2 className="my-4 text-xl font-semibold">Size variants</h2>
<VariantPreview title="Default (md)">
<AnimatedLogo />
</VariantPreview>

<VariantPreview title="Small (sm)">
<AnimatedLogo size="sm" />
</VariantPreview>

<VariantPreview title="Medium (md)">
<AnimatedLogo size="md" />
</VariantPreview>

<VariantPreview title="Large (lg)">
<AnimatedLogo size="lg" />
</VariantPreview>

<VariantPreview title="Extra Large (xl)">
<AnimatedLogo size="xl" />
</VariantPreview>
</div>

<div className="flex flex-col gap-4">
<h2 className="my-4 text-xl font-semibold">Wordmark variants</h2>
<VariantPreview title="Default (md)">
<AnimatedLogo size="md" variant="icon-with-wordmark" />
</VariantPreview>

<VariantPreview title="Small (sm)">
<AnimatedLogo size="sm" variant="icon-with-wordmark" />
</VariantPreview>

<VariantPreview title="Medium (md)">
<AnimatedLogo size="md" variant="icon-with-wordmark" />
</VariantPreview>

<VariantPreview title="Large (lg)">
<AnimatedLogo size="lg" variant="icon-with-wordmark" />
</VariantPreview>

<VariantPreview title="Extra Large (xl)">
<AnimatedLogo size="xl" variant="icon-with-wordmark" />
</VariantPreview>
</div>
</div>
),
}
193 changes: 193 additions & 0 deletions src/components/Logo/Animated.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
import { useTheme } from '@/hooks/useTheme'
import { cn } from '@/lib/utils'
import {
Fit,
Layout,
useRive,
useViewModelInstanceBoolean,
} from '@rive-app/react-canvas-lite'
import { cva } from 'class-variance-authority'
import { FC, useEffect, useState } from 'react'

// read rive file from local directory into arraybuffer
const riveFile = await import('./speakeasy-logo.riv')

const stackLogoClass = cva('relative', {
variants: {
size: {
xl: 'size-20',
lg: 'size-16',
md: 'size-8',
sm: 'size-6',
},
},
})

type Size = 'sm' | 'md' | 'lg' | 'xl'

type StateMachine = 'loop-dark' | 'loop-light'

const lettersLogoClass = cva('hidden mt-px md:block', {
variants: {
size: {
xl: 'ml-4 w-[22rem] h-auto',
lg: 'ml-3 w-[19.75rem] h-auto',
md: 'ml-2 w-[9.75rem] h-auto',
sm: 'ml-2 w-[8.75rem] h-auto',
},
},
})

export const AnimatedLogo = ({
className,
size = 'md',
variant = 'icon',
onMouseEnter,
onMouseLeave,
...rest
}: {
className?: string
size?: Size
variant?: 'icon' | 'icon-with-wordmark'
} & React.HTMLAttributes<HTMLDivElement>) => {
const [riveLoaded, setRiveLoaded] = useState(false)
const theme = useTheme()
const [stateMachine, setStateMachine] = useState<StateMachine>('loop-dark')
const { rive, RiveComponent } = useRive({
src: riveFile.default,
stateMachines: stateMachine,
autoplay: true,
layout: new Layout({
fit: Fit.Contain,
}),
onLoad: () => {
setRiveLoaded(true)
},
})

const { setValue: setIsHovered } = useViewModelInstanceBoolean(
'isHovered',
rive?.viewModelInstance
)

useEffect(() => {
setStateMachine(theme === 'dark' ? 'loop-dark' : 'loop-light')
}, [theme])

useEffect(() => {
return () => {
rive?.cleanup()
}
}, [rive])

return (
<div
className={cn('flex items-center', className)}
onMouseEnter={(e) => {
setIsHovered(true)
onMouseEnter?.(e)
}}
onMouseLeave={(e) => {
setIsHovered(false)
onMouseLeave?.(e)
}}
{...rest}
>
<div className={cn(stackLogoClass({ size }))}>
<LogoSvg
aria-hidden
className={cn(
'pointer-events-none absolute top-0 left-0 size-full scale-[0.87] transition-opacity duration-100',
{
'opacity-0': riveLoaded,
}
)}
/>
<RiveComponent key={stateMachine} className="size-full" />
</div>
{variant === 'icon-with-wordmark' && <WordmarkSvg size={size} />}
</div>
)
}

const WordmarkSvg: FC<{ size: Size }> = ({ size = 'md' }) => (
<>
<p className="sr-only">Speakeasy Logo</p>
<svg
width="106"
height="22"
viewBox="0 0 106 22"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className={lettersLogoClass({ size })}
>
<path
d="M102.372 4.72596L98.9819 13.7316L95.4811 4.72596H93.1875L97.8247 16.2969L97.2496 17.6066C97.087 17.995 96.9453 18.2702 96.8328 18.4212L96.8231 18.4347C96.7314 18.5885 96.6105 18.6937 96.4535 18.7557C96.2882 18.8232 96.0284 18.8569 95.6811 18.8569H93.8696V20.7964H96.2549C96.8106 20.7964 97.2634 20.7087 97.6038 20.5361C97.9455 20.3621 98.2345 20.091 98.4596 19.7295C98.6763 19.3829 98.9277 18.8596 99.2042 18.1731L104.665 4.72461H102.37L102.372 4.72596Z"
fill="var(--foreground)"
/>
<path
d="M91.0858 9.92009C90.4231 9.71103 89.566 9.54378 88.538 9.42509C87.8114 9.33742 87.2613 9.25515 86.9001 9.18232C86.575 9.11353 86.3041 8.9827 86.0944 8.79253C85.8929 8.61044 85.7943 8.35148 85.7943 8.00081C85.7943 7.52604 86.0138 7.15513 86.4681 6.86515C86.9307 6.55629 87.553 6.39983 88.3143 6.39983C89.0756 6.39983 89.6855 6.58461 90.1786 6.95012C90.6718 7.31699 90.9469 7.76747 91.0191 8.32855L91.0344 8.44724H93.1863L93.1752 8.30158C93.0807 7.10928 92.5764 6.17729 91.6762 5.53393C90.7996 4.89597 89.6466 4.57227 88.2463 4.57227C87.3905 4.57227 86.607 4.72602 85.9207 5.02815C85.2289 5.31813 84.6704 5.73759 84.2592 6.27575C83.8619 6.8166 83.6605 7.44107 83.6605 8.13433C83.6605 8.90717 83.873 9.52895 84.2939 9.98483C84.7065 10.4164 85.2511 10.7347 85.9138 10.9344C86.5598 11.1286 87.4016 11.2877 88.413 11.4064C89.1228 11.4941 89.673 11.5831 90.0494 11.6708C90.419 11.753 90.7204 11.9068 90.9455 12.124C91.1705 12.3411 91.272 12.6459 91.272 13.0519C91.272 13.5267 91.0372 13.9043 90.5565 14.2064C90.0772 14.5018 89.4312 14.6529 88.6366 14.6529C87.7392 14.6529 86.996 14.4452 86.4278 14.0351C85.8763 13.6265 85.5943 13.1139 85.5651 12.4679L85.5595 12.3384H83.4062V12.4746C83.4243 13.729 83.9119 14.723 84.8621 15.4298C85.8137 16.1271 87.0835 16.4804 88.6366 16.4804C89.5063 16.4804 90.3106 16.3348 91.0274 16.0475C91.7498 15.7575 92.3319 15.3394 92.7584 14.8039C93.1877 14.2631 93.4058 13.6292 93.4058 12.9184C93.4058 12.1024 93.1863 11.4442 92.7542 10.9613C92.3416 10.4839 91.7804 10.1332 91.0872 9.91874L91.0858 9.92009Z"
fill="var(--foreground)"
/>
<path
d="M82.037 14.2455C81.9314 14.1349 81.8786 13.9568 81.8786 13.7194V8.82345C81.8786 7.4693 81.4465 6.41592 80.595 5.69703C79.7628 4.96601 78.5667 4.59375 77.0414 4.59375C75.6119 4.59375 74.4283 4.91745 73.5211 5.55406C72.6223 6.19742 72.0944 7.10514 71.9527 8.25023L71.9346 8.40129H74.0837L74.106 8.29339C74.2213 7.74579 74.5172 7.32228 75.0117 6.99723C75.5202 6.6587 76.1731 6.4874 76.9511 6.4874C77.8304 6.4874 78.5167 6.68567 78.9946 7.07951C79.485 7.472 79.7239 8.0061 79.7239 8.7115V9.35486H76.4246C74.8714 9.35486 73.6628 9.67182 72.8432 10.2882L72.8307 10.2949C72.0041 10.9275 71.5859 11.846 71.5859 13.0275C71.5859 14.0903 71.9971 14.9414 72.8098 15.5605C73.6253 16.1701 74.7103 16.479 76.0342 16.479C77.6512 16.479 78.9321 15.9206 79.8462 14.8173C79.9142 15.257 80.0865 15.609 80.3616 15.8626C80.7075 16.1823 81.2798 16.3455 82.062 16.3455H83.2317V14.406H82.5204C82.2995 14.406 82.1412 14.3534 82.0384 14.2441L82.037 14.2455ZM79.7211 11.1622V11.583C79.7211 12.5042 79.4044 13.2568 78.7807 13.8206C78.1527 14.3736 77.2747 14.6528 76.1703 14.6528C75.4257 14.6528 74.8297 14.4896 74.3977 14.1686C73.9712 13.8516 73.7642 13.4429 73.7642 12.9183C73.7642 12.3275 73.9601 11.8986 74.3616 11.6073C74.77 11.3119 75.4021 11.1608 76.2384 11.1608H79.7211V11.1622Z"
fill="var(--foreground)"
/>
<path
d="M67.9452 5.25464C67.1033 4.8163 66.1253 4.59375 65.0376 4.59375C63.9498 4.59375 62.9371 4.84732 62.0786 5.34636C61.22 5.83191 60.5379 6.53056 60.0545 7.42479C59.5877 8.31632 59.3516 9.36295 59.3516 10.5364C59.3516 11.7098 59.5961 12.7416 60.0795 13.6493C60.5782 14.5422 61.2826 15.2489 62.173 15.7493C63.0608 16.2335 64.1096 16.479 65.2904 16.479C66.6463 16.479 67.8091 16.1013 68.7496 15.3568C69.6873 14.5975 70.2819 13.6129 70.5166 12.43L70.5486 12.2695H68.3717L68.3439 12.3693C68.155 13.0612 67.7716 13.6129 67.2062 14.0081C66.6519 14.3898 65.9614 14.584 65.1543 14.584C64.1138 14.584 63.2594 14.2603 62.6176 13.6237C61.9744 12.9844 61.6326 12.1171 61.6021 11.0462V11.026H70.6681L70.6792 10.9019C70.7097 10.5377 70.725 10.2801 70.725 10.1088C70.6945 8.99474 70.4333 8.00745 69.9512 7.17527C69.4664 6.34039 68.7926 5.69434 67.948 5.25329L67.9452 5.25464ZM68.4814 9.24561H61.7327C61.8452 8.44715 62.2092 7.78626 62.8135 7.27912C63.4567 6.73962 64.2124 6.46582 65.0612 6.46582C66.0156 6.46582 66.8102 6.72479 67.4229 7.23462C68.0147 7.71612 68.3703 8.39185 68.4801 9.24561H68.4814Z"
fill="var(--foreground)"
/>
<path
d="M59.6949 4.72835H57.0679L51.3291 10.6224V0.5H49.1716V14.4084H47.5879C47.3671 14.4084 47.2087 14.3558 47.1059 14.2465C47.0003 14.1359 46.9475 13.9579 46.9475 13.7205V8.82453C46.9475 7.47037 46.5155 6.417 45.6639 5.69811C44.8317 4.96708 43.6356 4.59483 42.1103 4.59483C40.6808 4.59483 39.4972 4.91853 38.59 5.55514C37.6912 6.1985 37.1633 7.10621 37.0216 8.2513L37.0036 8.40237H39.1527L39.1749 8.29447C39.2902 7.74687 39.5861 7.32336 40.0806 6.99831C40.5891 6.65977 41.242 6.48713 42.02 6.48713C42.8994 6.48713 43.5856 6.6854 44.0635 7.07924C44.5539 7.47172 44.7928 8.00448 44.7928 8.71123V9.35459H41.4935C39.9403 9.35459 38.7317 9.67155 37.901 10.296C37.0744 10.9286 36.6562 11.8471 36.6562 13.0286C36.6562 14.0914 37.0675 14.9425 37.8801 15.5616C38.6956 16.1712 39.7806 16.4801 41.1045 16.4801C42.7215 16.4801 44.0024 15.9217 44.9165 14.8184C44.9846 15.2581 45.1568 15.6088 45.4319 15.8637C45.7778 16.1833 46.3501 16.3465 47.1323 16.3465H49.1744H51.3319V13.2606L53.5143 11.0378L57.3138 16.2885L57.3555 16.3465H59.963L55.0619 9.52588L59.6977 4.727L59.6949 4.72835ZM39.4305 11.6084C39.8389 11.313 40.471 11.1619 41.3073 11.1619H44.7901V11.5827C44.7901 12.5039 44.4733 13.2565 43.8496 13.8203C43.2216 14.3733 42.3437 14.6525 41.2392 14.6525C40.4946 14.6525 39.8987 14.4893 39.4666 14.1683C39.0401 13.8513 38.8331 13.4427 38.8331 12.918C38.8331 12.3272 39.029 11.8983 39.4305 11.607V11.6084Z"
fill="var(--foreground)"
/>
<path
d="M33.0116 5.25464C32.1698 4.8163 31.1918 4.59375 30.104 4.59375C29.0163 4.59375 28.0035 4.84732 27.145 5.34636C26.2851 5.83191 25.6044 6.53056 25.1209 7.42479C24.6541 8.31632 24.418 9.36295 24.418 10.5364C24.418 11.7098 24.6625 12.7416 25.1459 13.6493C25.6446 14.5422 26.349 15.2489 27.2395 15.7493C28.1272 16.2335 29.176 16.479 30.3555 16.479C31.7113 16.479 32.8755 16.1013 33.8146 15.3568C34.7523 14.5975 35.3469 13.6129 35.5817 12.43L35.6136 12.2695H33.4367L33.4089 12.3693C33.22 13.0612 32.8366 13.6129 32.2712 14.0081C31.7169 14.3898 31.0264 14.584 30.2193 14.584C29.1788 14.584 28.3244 14.2603 27.6826 13.6237C27.0394 12.9844 26.6977 12.1171 26.6685 11.0462V11.026H35.7345L35.7456 10.9019C35.7761 10.5377 35.7914 10.2801 35.7914 10.1088C35.7609 8.99339 35.4997 8.00745 35.0176 7.17527C34.5328 6.34039 33.859 5.69434 33.0144 5.25329L33.0116 5.25464ZM33.5479 9.24561H26.7991C26.9116 8.44715 27.2756 7.78626 27.8799 7.27912C28.5231 6.73962 29.2788 6.46582 30.1276 6.46582C31.082 6.46582 31.8766 6.72479 32.4893 7.23462C33.0811 7.71612 33.4367 8.39185 33.5465 9.24561H33.5479Z"
fill="var(--foreground)"
/>
<path
d="M20.7354 5.3005C19.8783 4.83113 18.8836 4.59375 17.7792 4.59375C16.1593 4.59375 14.8424 5.18451 13.8616 6.34848L13.6379 4.72728H11.75V20.7977H13.9074V14.8942C14.2714 15.3326 14.734 15.6954 15.2855 15.9759H15.2883C15.9774 16.3118 16.8151 16.4803 17.7806 16.4803C18.8711 16.4803 19.8658 16.2268 20.7396 15.7277C21.6134 15.2274 22.3024 14.5206 22.7859 13.6277C23.2832 12.7362 23.5361 11.6963 23.5361 10.5377C23.5361 9.31845 23.2832 8.24888 22.7859 7.3587C22.301 6.46448 21.612 5.77121 20.7368 5.30185L20.7354 5.3005ZM21.33 10.5377C21.33 11.73 20.9799 12.7173 20.2881 13.4753C19.6157 14.2131 18.7099 14.5867 17.5958 14.5867C16.8762 14.5867 16.2219 14.414 15.6523 14.0728C15.0966 13.7316 14.659 13.2487 14.352 12.6377C14.0422 12.0065 13.8838 11.2849 13.8838 10.4932C13.8838 9.70149 14.0408 9.01767 14.3506 8.41613C14.6576 7.80514 15.0952 7.32903 15.6509 7.00263C16.2219 6.66139 16.8762 6.48875 17.5958 6.48875C18.7099 6.48875 19.6157 6.87045 20.2895 7.62441C20.9799 8.36622 21.3314 9.34677 21.3314 10.5377H21.33Z"
fill="var(--foreground)"
/>
<path
d="M8.1483 9.92009C7.48565 9.71103 6.6285 9.54378 5.60049 9.42509C4.87532 9.33742 4.32381 9.25515 3.96261 9.18232C3.63754 9.11353 3.36664 8.9827 3.15687 8.79253C2.95544 8.61044 2.8568 8.35148 2.8568 8.00081C2.8568 7.52604 3.0763 7.15513 3.53057 6.86515C3.99318 6.55629 4.61554 6.39983 5.37683 6.39983C6.13812 6.39983 6.74798 6.58461 7.24115 6.95012C7.73431 7.31564 8.00938 7.76747 8.08162 8.32855L8.0969 8.44724H10.2488L10.2377 8.30158C10.1432 7.10928 9.63892 6.17729 8.73871 5.53393C7.86212 4.89597 6.70908 4.57227 5.30875 4.57227C4.453 4.57227 3.66949 4.72602 2.98322 5.02815C2.29139 5.31813 1.73293 5.73759 1.32173 6.27575C0.924413 6.81525 0.722976 7.44107 0.722976 8.13433C0.722976 8.90717 0.935526 9.52895 1.35646 9.98483C1.76905 10.4164 2.31362 10.7347 2.97627 10.9344C3.62226 11.1286 4.46412 11.2877 5.47546 11.4064C6.18535 11.4941 6.73547 11.5831 7.11195 11.6708C7.48148 11.753 7.78294 11.9068 8.00799 12.124C8.22471 12.3344 8.33445 12.6459 8.33445 13.0519C8.33445 13.5267 8.09968 13.9043 7.61901 14.2064C7.13973 14.5018 6.49375 14.6529 5.69912 14.6529C4.80169 14.6529 4.05847 14.4452 3.49028 14.0351C2.93876 13.6265 2.65676 13.1139 2.62758 12.4679L2.62202 12.3384H0.46875V12.4746C0.485421 13.729 0.974422 14.723 1.92325 15.4298C2.87486 16.1271 4.14599 16.4804 5.69774 16.4804C6.56738 16.4804 7.37173 16.3348 8.08856 16.0475C8.81095 15.7575 9.39303 15.3394 9.81952 14.8039C10.2488 14.2631 10.4669 13.6278 10.4669 12.9184C10.4669 12.1024 10.2474 11.4442 9.81535 10.9613C9.40276 10.4839 8.84152 10.1332 8.14691 9.91874L8.1483 9.92009Z"
fill="var(--foreground)"
/>
</svg>
</>
)

const LogoSvg: FC<JSX.IntrinsicElements['svg']> = (props) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="30"
height="30"
viewBox="0 0 30 30"
fill="none"
{...props}
>
<title>Speakeasy Logo</title>
<path
fill="var(--foreground)"
d="m20.639 27.548-19.17-2.724L0 26.1l20.639 2.931 8.456-7.336-1.468-.208-6.988 6.062Z"
/>
<path
fill="var(--foreground)"
d="m20.639 23.1 8.456-7.336-1.468-.207-6.988 6.06-6.84-.972-9.394-1.333-2.936-.417L0 20.169l2.937.416L0 23.132l20.639 2.931 8.456-7.334-1.468-.208-6.986 6.062-9.78-1.39 1.468-1.273 8.31 1.18Z"
/>
<path
fill="var(--foreground)"
d="m20.639 18.65-19.17-2.724L0 17.201l20.639 2.931 8.456-7.334-1.468-.208-6.988 6.06Z"
/>
<path
fill="var(--foreground)"
d="M27.627 6.658 24.69 9.205 20.64 12.72l-7.923-1.126L1.469 9.996 0 11.271l11.246 1.596-1.467 1.275-8.311-1.181L0 14.235l20.639 2.932 8.456-7.334-2.937-.418 2.937-2.549-1.468-.208Z"
/>
<path
fill="var(--foreground)"
d="M29.095 3.902 8.456.971 0 8.305l20.639 2.934 8.456-7.337Z"
/>
</svg>
)
}
Binary file added src/components/Logo/speakeasy-logo.riv
Binary file not shown.
6 changes: 6 additions & 0 deletions src/vite-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/// <reference types="vite/client" />

declare module '*.riv' {
const value: string
export default value
}
1 change: 1 addition & 0 deletions vite.config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import tailwindcss from '@tailwindcss/vite'
const packageName = 'moonshine'

export default defineConfig({
assetsInclude: ['**/*.riv'],
plugins: [
react(),
dts(),
Expand Down