feat: update font usage across components to include Syne and Unbounded, enhancing typography consistency

This commit is contained in:
shiva raj badu 2026-03-04 11:44:10 +05:45
parent ed76eff535
commit 86ea9a91da
No known key found for this signature in database
35 changed files with 244 additions and 145 deletions

View file

@ -42,39 +42,7 @@ const DashboardSidebar = () => {
<img src="/logo-with-bg.png" alt="Presenton logo" className="h-[40px] object-contain w-full" />
</div>
</div>
{/* <div className="mt-3">
{mounted && (auth?.user || auth?.userEmail) ? (
<Link
prefetch={false}
href="/profile"
className="w-full flex gap-3 items-center cursor-pointer rounded-2xl ring-1 ring-inset ring-slate-200 bg-white/80 hover:bg-white transition-colors px-3 py-2"
aria-label="Open profile"
title="Profile"
>
<div className="h-8 w-8 rounded-full bg-[#5146E5]/10 flex items-center justify-center text-[#5146E5] text-xs font-semibold">
{(auth?.user?.name?.[0] || auth?.userEmail?.[0] || "?").toUpperCase()}
</div>
<div className="min-w-0 text-left">
<div className="text-xs font-semibold text-slate-900 truncate">{auth?.user?.name || auth?.userEmail}</div>
{auth?.userEmail && <div className="text-[10px] text-slate-500 truncate">{auth.userEmail}</div>}
</div>
</Link>
) : (
<div
className="w-full flex items-center cursor-pointer rounded-2xl ring-1 ring-inset ring-slate-200 bg-white/80 px-3 py-2 gap-3"
>
<UserRoundCog className="h-4 w-4 text-slate-700" />
<div className="flex-1">
<div className="bg-slate-100 animate-pulse rounded w-full h-4 mb-1"></div>
<div className="bg-slate-100 animate-pulse rounded w-2/3 h-3"></div>
</div>
</div>
)}
</div> */}
<nav className="pt-6" aria-label="Dashboard sections">
<nav className="pt-6 font-syne" aria-label="Dashboard sections">
<div className=" space-y-6">
{/* Dashboard */}
@ -125,7 +93,7 @@ const DashboardSidebar = () => {
</nav>
</div>
<div className=" pt-5 border-t border-slate-200/60 "
<div className=" pt-5 border-t border-slate-200/60 font-syne "
>
{BelongingNavItems.map(({ key, label: itemLabel, icon: Icon }) => {
const isActive = activeTab === key;

View file

@ -57,9 +57,9 @@ const DashboardPage: React.FC = () => {
{<Link
<Link
href="/generate"
className="inline-flex items-center gap-2 rounded-xl px-4 py-2.5 text-black text-sm font-medium shadow-sm hover:shadow-md"
className="inline-flex items-center gap-2 rounded-xl px-4 py-2.5 text-black text-sm font-semibold font-syne shadow-sm hover:shadow-md"
aria-label="Create new presentation"
style={{
borderRadius: "48px",
@ -70,7 +70,7 @@ const DashboardPage: React.FC = () => {
<span className="hidden md:inline">New presentation</span>
<span className="md:hidden">New</span>
<ChevronRight className="w-4 h-4" />
</Link>}
</Link>
{/* {
<Link
href="/theme?tab=new-theme"

View file

@ -14,7 +14,7 @@ import { toast } from "sonner";
import { useFontLoader } from "@/app/(presentation-generator)/hooks/useFontLoader";
import SlideScale from "@/app/(presentation-generator)/components/PresentationRender";
import MarkdownRenderer from "@/app/(presentation-generator)/documents-preview/components/MarkdownRenderer";
import MarkdownRenderer from "@/components/MarkDownRender";
export const PresentationCard = ({
id,
@ -28,7 +28,7 @@ export const PresentationCard = ({
onDeleted?: (presentationId: string) => void;
}) => {
const router = useRouter();
// useFontLoader(presentation.fonts || []);
useFontLoader(presentation.fonts || []);
const handlePreview = (e: React.MouseEvent) => {
e.preventDefault();
router.push(`/presentation?id=${id}&type=standard`);
@ -57,7 +57,7 @@ export const PresentationCard = ({
<Card
suppressHydrationWarning={true}
onClick={handlePreview}
className="bg-[#F8FBFB] shadow-none sm:shadow-none presentation-card rounded-[12px] p-0 group hover:shadow-md transition-all duration-500 slide-theme cursor-pointer overflow-hidden flex flex-col"
className="bg-[#F8FBFB] font-syne shadow-none sm:shadow-none presentation-card rounded-[12px] p-0 group hover:shadow-md transition-all duration-500 slide-theme cursor-pointer overflow-hidden flex flex-col"
>
<div suppressHydrationWarning={true} className="flex flex-col flex-1 relative z-40">
{/* <p className=" text-xs font-syne absolute top-2 flex gap-1 capitalize items-center left-2 rounded-[100px] px-2.5 py-1 bg-[#3A3A3AF5] text-white font-semibold z-40 ">
@ -75,7 +75,7 @@ export const PresentationCard = ({
<div className="flex items-center justify-between gap-7 w-full">
<div className="flex flex-col items-start gap-1">
<div className="text-sm text-[#191919] font-semibold overflow-hidden line-clamp-2">
{<MarkdownRenderer content={title} />}
<MarkdownRenderer content={title} className="text-sm mb-0 text-[#191919] font-semibold overflow-hidden line-clamp-2" />
</div>
<p className="text-[#808080] text-sm font-syne">
{new Date(presentation?.created_at).toLocaleDateString()}

View file

@ -42,7 +42,7 @@ export const PresentationGrid = ({
const CreateNewCard = () => (
<div
onClick={handleCreateNewPresentation}
className="flex flex-col cursor-pointer group ring-1 ring-inset ring-slate-200 hover:ring-[#8A7DFF]/40 bg-white/80 rounded-xl overflow-hidden transition-all duration-300"
className="flex flex-col cursor-pointer group ring-1 ring-inset ring-slate-200 hover:ring-[#8A7DFF]/40 bg-white/80 rounded-xl overflow-hidden transition-all duration-300 font-syne"
>
<img src="/create_presentation.png" alt="New Presentation" className="w-full aspect-[16/11] object-cover" />
<div className="flex items-center gap-3 p-3 mt-auto border border-[#EDEEEF]">

View file

@ -48,11 +48,6 @@ const ImageProvider = ({ llmConfig, setLlmConfig }: { llmConfig: LLMConfig, setL
return "";
};
const shouldHideImageApiKeyInput = (providerValue: string, providerApiKeyField?: string) => {
if (!providerApiKeyField) return true;
if (providerValue === "comfyui") return false;
return providerApiKeyField === getTextProviderApiField();
};
@ -123,7 +118,7 @@ const ImageProvider = ({ llmConfig, setLlmConfig }: { llmConfig: LLMConfig, setL
<div className='flex justify-end items-center'>
<Switch
checked={!isImageGenerationDisabled}
className=''
className='data-[state=checked]:bg-[#4791FF] data-[state=unchecked]:bg-gray-400'
onCheckedChange={(checked) => handleChangeImageGenerationDisabled(!checked)}
/>
</div>
@ -236,10 +231,7 @@ const ImageProvider = ({ llmConfig, setLlmConfig }: { llmConfig: LLMConfig, setL
(() => {
const provider = IMAGE_PROVIDERS[llmConfig.IMAGE_PROVIDER];
// Show info message when using same API key as main provider
if (shouldHideImageApiKeyInput(provider.value, provider.apiKeyField)) {
return <></>;
}
// Show ComfyUI configuration
if (provider.value === "comfyui") {
@ -305,7 +297,7 @@ const ImageProvider = ({ llmConfig, setLlmConfig }: { llmConfig: LLMConfig, setL
</>
)}
</div>
<div className='flex justify-end items-center mt-[18px]'>
{!isImageGenerationDisabled && <div className='flex justify-end items-center mt-[18px]'>
{renderQualitySelector(llmConfig, input_field_changed)}
{llmConfig.IMAGE_PROVIDER === "comfyui" && <div className='w-full'>
@ -328,7 +320,7 @@ const ImageProvider = ({ llmConfig, setLlmConfig }: { llmConfig: LLMConfig, setL
</div>
</div>}
</div>
</div>}
</div>
</div>
</div>

View file

@ -183,7 +183,7 @@ const SettingsPage = () => {
: "No image provider";
return (
<div className="h-screen font-instrument_sans flex flex-col overflow-hidden relative">
<div className="h-screen font-syne flex flex-col overflow-hidden relative">
<div
className='fixed z-0 bottom-[-14.5rem] left-0 w-full h-full'
style={{
@ -236,7 +236,7 @@ const SettingsPage = () => {
background: "linear-gradient(270deg, #D5CAFC 2.4%, #E3D2EB 27.88%, #F4DCD3 69.23%, #FDE4C2 100%)",
color: "#101323",
}}
className={`w-full flex items-center justify-center gap-2 font-semibold py-3 px-5 rounded-[58px] transition-all duration-500 ${buttonState.isDisabled
className={`w-full font-syne font-semibold flex items-center justify-center gap-2 py-3 px-5 rounded-[58px] transition-all duration-500 ${buttonState.isDisabled
? "bg-gray-400 cursor-not-allowed"
: "bg-gradient-to-r from-blue-600 to-indigo-600 hover:from-blue-700 hover:to-indigo-700 focus:ring-4 focus:ring-blue-200"
} text-white`}

View file

@ -81,7 +81,9 @@ const TextProvider = ({
if (currentModelField) {
onInputChange('', currentModelField);
}
}, [selectedProvider, currentApiKey, currentCustomUrl, currentOllamaUrl]);
}, [selectedProvider, currentApiKey, currentCustomUrl, currentModelField]);
const onApiKeyChange = (llm: keyof typeof LLM_PROVIDERS, value: string) => {
if (llm === 'ollama') {

View file

@ -9,7 +9,7 @@ const CreateCustomTemplate = () => {
onClick={() => {
router.push('/custom-template')
}}
className='w-full rounded-xl border border-[#EDEEEF] cursor-pointer'>
className='w-full rounded-xl border border-[#EDEEEF] cursor-pointer font-syne'>
<div className='relative h-[215px] flex justify-center items-center '>
<img src="/card_bg.svg" alt="" className="absolute top-0 z-[1] left-0 w-full h-full object-cover" />
<div className='w-[36px] h-[36px] relative z-[4] rounded-full bg-[#7A5AF8] flex items-center justify-center'

View file

@ -191,7 +191,7 @@ const LayoutPreview = () => {
);
return (
<div className="min-h-screen relative">
<div className="min-h-screen relative font-syne">
<div
className='fixed z-0 bottom-[-16.5rem] left-0 w-full h-full'
style={{
@ -212,8 +212,8 @@ const LayoutPreview = () => {
<Link
href="/custom-template"
className="inline-flex items-center font-inter font-normal gap-2 rounded-xl px-4 py-2.5 text-black text-sm shadow-sm hover:shadow-md"
aria-label="Create new themes"
className="inline-flex items-center font-syne font-semibold gap-2 rounded-xl px-4 py-2.5 text-black text-sm shadow-sm hover:shadow-md"
aria-label="Create new template"
style={{
borderRadius: "48px",
background: "linear-gradient(270deg, #D5CAFC 2.4%, #E3D2EB 27.88%, #F4DCD3 69.23%, #FDE4C2 100%)",

View file

@ -31,6 +31,7 @@ import { useSearchParams } from 'next/navigation'
import CustomTabEmpty from './CustomTabEmpty'
import ThemeApi from '@/app/(presentation-generator)/services/api/theme'
import { useFontLoader } from '@/app/(presentation-generator)/hooks/useFontLoad'
import Link from 'next/link'
// Fallback theme used before defaults are loaded from API (unified Theme type)
const FALLBACK_THEME: Theme = {
@ -844,11 +845,27 @@ const ThemePanel: React.FC = () => {
return (
<div className="space-y-6 px-6">
<div className="space-y-6 px-6 font-syne">
<div className='py-[28px] flex justify-between'>
<h3 className=" text-[28px] py-[28px] tracking-[-0.84px] font-unbounded font-normal text-[#101828] flex items-center gap-2">
Themes
</h3>
<h3 className=" text-[28px] tracking-[-0.84px] font-unbounded font-normal text-[#101828] flex items-center gap-2">
Themes
</h3>
<Link
href="/theme?tab=new-theme"
className="inline-flex items-center gap-2 rounded-xl px-4 py-2.5 text-black text-sm font-semibold font-syne shadow-sm hover:shadow-md"
aria-label="Create new theme"
style={{
borderRadius: "48px",
background: "linear-gradient(270deg, #D5CAFC 2.4%, #E3D2EB 27.88%, #F4DCD3 69.23%, #FDE4C2 100%)",
}}
>
<span className="hidden md:inline">New Theme</span>
<span className="md:hidden">New</span>
<ChevronRight className="w-4 h-4" />
</Link>
</div>
{/* Tabs */}
<div className='p-1 rounded-[40px] bg-[#F7F6F9] w-fit border border-[#F4F4F4] flex items-center justify-center '>
<button className='px-5 py-2 text-xs font-medium text-[#3A3A3A] rounded-[70px]'
@ -908,7 +925,7 @@ const ThemePanel: React.FC = () => {
<Sheet open={isSheetOpen} onOpenChange={handleCloseSheet} >
<SheetContent side="bottom" className="h-[90vh] w-full p-0 [&>button]:hidden focus:outline-none" >
<SheetContent side="bottom" className="h-[90vh] font-syne w-full p-0 [&>button]:hidden focus:outline-none" >
<div className="flex h-full">
{/* Left side - Editor */}
<div

View file

@ -34,7 +34,7 @@ export const CustomTemplateCard = memo(({ template, onSelectTemplate, selectedTe
return (
<Card
className={`${isSelected ? 'border-2 border-blue-500' : ''} cursor-pointer flex flex-col justify-between relative hover:shadow-lg transition-all duration-200 group overflow-hidden`}
className={`${isSelected ? 'border-2 border-blue-500' : ''} font-syne cursor-pointer flex flex-col justify-between relative hover:shadow-lg transition-all duration-200 group overflow-hidden`}
onClick={() => onSelectTemplate(template.id)}
>

View file

@ -9,7 +9,7 @@ const EmptyStateView: React.FC = () => {
return (
<Wrapper>
<div className="max-w-[800px] h-[calc(100vh-72px)] flex justify-center items-center mx-auto px-4 sm:px-6 pb-6">
<div className="max-w-[800px] h-[calc(100vh-72px)] font-syne flex justify-center items-center mx-auto px-4 sm:px-6 pb-6">
<div className="text-center space-y-8">
{/* Icon */}
<div className="flex justify-center">

View file

@ -51,7 +51,7 @@ const GenerateButton: React.FC<GenerateButtonProps> = ({
}
onSubmit();
}}
className=" w-full flex items-center gap-0.5 rounded-[58px] text-sm py-3 px-5 font-instrument_sans font-semibold text-[#101323] disabled:opacity-50 disabled:cursor-not-allowed"
className=" w-full flex items-center gap-0.5 rounded-[58px] text-sm py-3 px-5 font-instrument_sans font-semibold text-[#101323] disabled:opacity-50 disabled:cursor-not-allowed font-syne"
style={{
background: "linear-gradient(270deg, #D5CAFC 2.4%, #E3D2EB 27.88%, #F4DCD3 69.23%, #FDE4C2 100%)",
}}

View file

@ -48,7 +48,7 @@ const OutlineContent: React.FC<OutlineContentProps> = ({
const pathname = usePathname();
return (
<div className="space-y-6 font-instrument_sans">
<div className="space-y-6 font-syne ">
{isLoading && (!outlines || outlines.length === 0) && (
<div className="flex items-center justify-center">
<span className="inline-flex items-center gap-1 rounded-full border border-blue-200 bg-blue-50 text-blue-600 px-2 py-0.5 text-xs">

View file

@ -119,7 +119,7 @@ export function OutlineItem({
}, [isStreaming, isActiveStreaming, isStableStreaming, slideOutline.content])
return (
<div className="mb-4 bg-white rounded-[12px] group shadow-sm p-10 relative">
<div className="mb-4 bg-white rounded-[12px] group shadow-sm p-10 relative font-syne">
<div
ref={setNodeRef}

View file

@ -40,7 +40,7 @@ const OutlinePage: React.FC = () => {
return (
<div className="">
<div className=" font-syne">
<div
className='fixed z-0 bottom-[-16.5rem] left-0 w-full h-full'
style={{
@ -103,7 +103,7 @@ const OutlinePage: React.FC = () => {
</Tabs>
{/* Fixed Button */}
<div className="absolute bottom-0 -right-10 z-50">
<div className="absolute bottom-0 -right-5 z-50">
<GenerateButton
outlineCount={outlines.length}
loadingState={loadingState}

View file

@ -19,7 +19,7 @@ const BuiltInLayoutPreview = memo(({ layout, templateId, index }: {
const LayoutComponent = layout.component;
return (
<div
className="relative bg-gray-100 border border-gray-200 overflow-hidden aspect-video rounded"
className="relative bg-gray-100 font-syne border border-gray-200 overflow-hidden aspect-video rounded"
style={{ contain: 'layout style paint' }}
>
<div className="absolute inset-0 bg-transparent z-10" />
@ -66,10 +66,10 @@ const BuiltInTemplateCard = memo(({ template, isSelected, onSelect }: {
</div>
<div className="flex items-center justify-between p-5 bg-white border-t border-[#EDEEEF] relative z-40">
<div>
<h3 className="text-sm font-bold text-gray-900 capitalize">
<h3 className="text-sm font-bold text-gray-900 capitalize font-syne">
{template.name}
</h3>
<p className="text-xs text-gray-600 mb-4 line-clamp-2">
<p className="text-xs text-gray-600 mb-4 line-clamp-2 font-syne">
{template.description}
</p>
</div>
@ -130,8 +130,8 @@ const TemplateSelection: React.FC<TemplateSelectionProps> = memo(({
const customTemplateCards = useMemo(() => {
if (customLoading) {
return (
<div className="flex items-center justify-center py-12">
<Loader2 className="w-8 h-8 animate-spin text-blue-600" />
<div className="flex items-center justify-center py-12 font-syne">
<Loader2 className="w-8 h-8 animate-spin text-blue-600 font-syne" />
<span className="ml-3 text-gray-600">Loading custom templates...</span>
</div>
);
@ -177,13 +177,13 @@ const TemplateSelection: React.FC<TemplateSelectionProps> = memo(({
{/* Custom AI Templates */}
<div>
<div className="flex items-center justify-between mb-3">
<h3 className="text-lg font-semibold text-gray-900">Custom</h3>
<h3 className="text-lg font-semibold text-gray-900 font-syne">Custom</h3>
</div>
{customTemplateCards}
</div>
{/* In Built Templates */}
<div>
<h3 className="text-lg font-semibold text-gray-900 mb-3">In Built</h3>
<h3 className="text-lg font-semibold text-gray-900 mb-3 font-syne">In Built</h3>
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
{builtInTemplateCards}
</div>

View file

@ -214,8 +214,8 @@ const PresentationHeader = ({
return (
<>
<div className="py-7 sticky top-0 bg-white z-50 mb-[17px] pr-[25px] flex justify-between items-center">
<h2 className="text-lg text-[#101323] "><MarkdownRenderer content={presentationData?.title || "Presentation"} className="mb-0 w-[600px] truncate text-sm text-[#101323] " /></h2>
<div className="py-7 sticky top-0 bg-white z-50 mb-[17px] pr-[25px] font-syne flex justify-between items-center">
<h2 className="text-lg text-[#101323] font-unbounded "><MarkdownRenderer content={presentationData?.title || "Presentation"} className="mb-0 w-[600px] truncate text-sm text-[#101323] " /></h2>
<div className="flex items-center gap-2.5">
{isPresentationSaving && <div className="flex items-center gap-2">

View file

@ -103,7 +103,7 @@ const PresentationPage: React.FC<PresentationPageProps> = ({
if (error) {
return (
<div className="flex flex-col items-center justify-center h-screen bg-gray-100">
<div className="flex flex-col items-center justify-center h-screen bg-gray-100 font-syne">
<div
className="bg-white border border-red-300 text-red-700 px-6 py-8 rounded-lg shadow-lg flex flex-col items-center"
role="alert"
@ -120,7 +120,7 @@ const PresentationPage: React.FC<PresentationPageProps> = ({
}
return (
<div className="h-screen overflow-hidden ">
<div className="h-screen overflow-hidden font-syne ">
<div
style={{
background: "#ffffff",

View file

@ -143,7 +143,7 @@ const SlideContent = ({ slide, index, presentationId }: SlideContentProps) => {
<div
data-layout={slide.layout}
data-group={slide.layout_group}
className={` w-full group `}
className={` w-full group font-syne `}
>
<V1ContentRender slide={slide} isEditMode={true} theme={null} />
{!showNewSlideSelection && (
@ -186,7 +186,7 @@ const SlideContent = ({ slide, index, presentationId }: SlideContentProps) => {
<PopoverTrigger asChild>
<button
type="button"
className="flex px-3.5 py-2.5 items-center justify-center rounded-full bg-[#F7F6F9]"
className="flex px-3.5 py-2.5 items-center justify-center rounded-full bg-[#F7F6F9] font-syne"
>
<ToolTip content="Update slide using prompt">
<Pencil className="h-4 w-4" />
@ -197,7 +197,7 @@ const SlideContent = ({ slide, index, presentationId }: SlideContentProps) => {
side="bottom"
align="center"
sideOffset={12}
className="z-30 w-[340px] rounded-2xl border border-gray-200 bg-white p-0 shadow-2xl"
className="z-30 w-[340px] rounded-2xl border border-gray-200 bg-white p-0 shadow-2xl font-syne"
>
<div className="border-b border-gray-100 px-4 py-3">
<p className="text-sm font-semibold text-gray-900">Update slide</p>
@ -242,7 +242,7 @@ const SlideContent = ({ slide, index, presentationId }: SlideContentProps) => {
background: "linear-gradient(270deg, #D5CAFC 2.4%, #E3D2EB 27.88%, #F4DCD3 69.23%, #FDE4C2 100%)",
}}
className={`flex px-4 py-2.5 items-center justify-center rounded-full border ${slide?.speaker_note
className={`flex px-4 py-2.5 items-center justify-center rounded-full border font-syne ${slide?.speaker_note
? "border-violet-200 bg-violet-50 text-violet-700"
: "border-gray-200 bg-white text-gray-600"
}`}
@ -260,7 +260,7 @@ const SlideContent = ({ slide, index, presentationId }: SlideContentProps) => {
side="bottom"
align="center"
sideOffset={12}
className="z-30 w-[340px] rounded-2xl border border-gray-200 bg-white p-0 shadow-2xl"
className="z-30 w-[340px] rounded-2xl border border-gray-200 bg-white p-0 shadow-2xl font-syne"
>
<div className="border-b border-gray-100 px-4 py-3">
<p className="text-sm font-semibold text-gray-900">Speaker notes</p>
@ -277,7 +277,7 @@ const SlideContent = ({ slide, index, presentationId }: SlideContentProps) => {
<button
type="button"
onClick={onDeleteSlide}
className="flex px-4 py-2.5 items-center justify-center rounded-full border border-gray-200 bg-white text-gray-600"
className="flex px-4 py-2.5 items-center justify-center rounded-full border border-gray-200 bg-white text-gray-600 font-syne"
>
<ToolTip content="Delete slide">
<Trash className="h-4 w-4" />

View file

@ -12,7 +12,6 @@ const ThemeSelector = ({ presentation_id, current_theme, themes: allThemes }: {
const [currentTheme, setCurrentTheme] = useState<any>(current_theme)
const dispatch = useDispatch()
const router = useRouter()
const { presentationData } = useSelector((state: RootState) => state.presentationGeneration)
const applyTheme = async (theme: any) => {
const element = document.getElementById('presentation-slides-wrapper')
if (!element) return;
@ -80,7 +79,7 @@ const ThemeSelector = ({ presentation_id, current_theme, themes: allThemes }: {
return (
<Popover>
<PopoverTrigger>
<button className="text-sm px-[18px] py-2.5 gap-1.5 flex items-center font-inter border border-[#EDEEEF] bg-[#F6F6F9] text-black hover:text-blue-500 duration-300 rounded-[88px] font-medium">
<button className="text-sm px-[18px] py-2.5 gap-1.5 flex items-center border border-[#EDEEEF] bg-[#F6F6F9] text-black hover:text-blue-500 duration-300 rounded-[88px] font-medium font-syne">
<Palette className="h-4 w-4" /> Theme
</button>
</PopoverTrigger>

View file

@ -10,7 +10,7 @@ const page = () => {
const queryId = params.get("id");
if (!queryId) {
return (
<div className="flex flex-col items-center justify-center h-screen">
<div className="flex flex-col items-center justify-center h-screen font-syne">
<h1 className="text-2xl font-bold">No presentation id found</h1>
<p className="text-gray-500 pb-4">Please try again</p>
<Button onClick={() => router.push("/dashboard")}>Go to home</Button>

View file

@ -7,15 +7,15 @@ interface PromptInputProps {
}
export function PromptInput({ value, onChange }: PromptInputProps) {
const [showHint, setShowHint] = useState(false);
const handleChange = (val: string) => {
setShowHint(val.length > 0);
onChange(val);
};
return (
<div className="space-y-2">
<div className="space-y-2 font-syne">
<div className="relative">
<Textarea
value={value}

View file

@ -139,14 +139,14 @@ const SupportingDoc = ({
return (
<div className="space-y-2" data-testid="attachments-uploader">
<div className="flex items-center justify-between">
<p className="text-sm text-gray-600">
<p className="text-sm text-gray-600 font-syne">
{hasFiles ? `${filteredFiles.length} attachment${filteredFiles.length > 1 ? 's' : ''}` : 'No attachments yet'}
</p>
<button
type="button"
onClick={handleClearFiles}
disabled={!hasFiles}
className={`text-sm font-medium ${!hasFiles ? 'cursor-not-allowed text-gray-400' : 'text-red-600 hover:text-red-700'}`}
className={`text-sm font-medium font-syne ${!hasFiles ? 'cursor-not-allowed text-gray-400' : 'text-red-600 hover:text-red-700'}`}
data-testid="attachments-clear-button"
aria-disabled={!hasFiles}
>
@ -170,7 +170,7 @@ const SupportingDoc = ({
/>
<div className="flex flex-col items-center gap-2">
<Paperclip className="h-6 w-6 text-[#5146E5]" />
<p className="text-sm font-medium text-gray-800">
<p className="text-sm font-medium text-gray-800 font-syne">
Drag and drop PDF, TXT, PPTX, DOCX, or <span className="text-[#5146E5]">click to browse</span>
</p>
</div>
@ -194,10 +194,10 @@ const SupportingDoc = ({
)}
<div className="min-w-0 flex-1">
<p className="truncate text-sm font-medium text-gray-900" title={file.name}>
<p className="truncate text-sm font-medium text-gray-900 font-syne" title={file.name}>
{file.name}
</p>
<p className="text-xs text-gray-500">{formatFileSize(file.size)}</p>
<p className="text-xs text-gray-500 font-syne">{formatFileSize(file.size)}</p>
</div>
<button
@ -213,7 +213,7 @@ const SupportingDoc = ({
))}
</ul>
{filteredFiles.length !== files.length && (
<p className="mt-2 text-xs text-amber-600">
<p className="mt-2 text-xs text-amber-600 font-syne">
Some files were skipped. Only PDF, TXT, PPTX, and DOCX files are supported.
</p>
)}

View file

@ -205,8 +205,8 @@ const UploadPage = () => {
<div className="rounded-2xl border border-slate-200/70 bg-white/80 shadow-sm backdrop-blur supports-[backdrop-filter]:bg-white/60" >
<div className="flex flex-col gap-4 md:items-center md:flex-row justify-between p-4">
<div >
<h2 className="text-lg font-unbounded tracking-tight text-slate-900">Configuration</h2>
<p className="text-sm text-slate-500">Choose slides, tone, and language preferences.</p>
<h2 className="text-lg font-unbounded tracking-tight text-slate-900 ">Configuration</h2>
<p className="text-sm text-slate-500 font-syne">Choose slides, tone, and language preferences.</p>
</div>
<ConfigurationSelects
config={config}
@ -216,7 +216,7 @@ const UploadPage = () => {
<div className="border-t border-slate-200/70" />
<div className="p-4 md:p-6">
<h3 className="text-base font-normal font-unbounded text-slate-900 mb-2">Content</h3>
<h3 className="text-base font-normal font-unbounded text-slate-900 mb-2">Content</h3>
<div className="relative">
<PromptInput
value={config.prompt}
@ -241,7 +241,7 @@ const UploadPage = () => {
<div className="p-4 md:p-6">
<Button
onClick={handleGeneratePresentation}
className="w-full rounded-[28px] flex items-center justify-center py-5 bg-[#5141e5] text-white font-instrument_sans font-semibold text-lg hover:bg-[#5141e5]/85 focus-visible:ring-2 focus-visible:ring-[#5141e5]/40"
className="w-full rounded-[28px] flex items-center justify-center py-5 bg-[#5141e5] text-white font-syne font-semibold text-lg hover:bg-[#5141e5]/85 focus-visible:ring-2 focus-visible:ring-[#5141e5]/40"
data-testid="next-button"
>
<span>Generate Presentation</span>

View file

@ -46,7 +46,7 @@ const page = () => {
<div className="relative">
<Header />
<div className="flex flex-col items-center justify-center mb-8">
<h1 className="text-[64px] font-semibold font-instrument_sans text-[#101323] ">
<h1 className="text-[64px] font-normal font-unbounded text-[#101323] ">
AI Presentation
</h1>
<p className="text-xl font-syne text-[#101323CC]">Choose a design, set preferences, and generate polished slides.</p>

View file

@ -4,7 +4,7 @@
body {
font-family: var(--font-inter), var(--font-roboto), sans-serif;
font-family: var(--font-inter), var(--font-unbounded), var(--font-syne), sans-serif;
}
@layer utilities {
@ -449,7 +449,7 @@ thead {
}
.container__editor {
font-variant-ligatures: common-ligatures;
background-color: #fafafa;
border-radius: 3px;
@ -458,6 +458,7 @@ thead {
.container__editor textarea {
outline: 0;
}
/* Syntax highlighting */
.token.comment,
.token.prolog,
@ -465,12 +466,15 @@ thead {
.token.cdata {
color: #90a4ae;
}
.token.punctuation {
color: #9e9e9e;
}
.namespace {
opacity: 0.7;
}
.token.property,
.token.tag,
.token.boolean,
@ -480,6 +484,7 @@ thead {
.token.deleted {
color: #e91e63;
}
.token.selector,
.token.attr-name,
.token.string,
@ -488,6 +493,7 @@ thead {
.token.inserted {
color: #4caf50;
}
.token.operator,
.token.entity,
.token.url,
@ -495,26 +501,32 @@ thead {
.style .token.string {
color: #795548;
}
.token.atrule,
.token.attr-value,
.token.keyword {
color: #3f51b5;
}
.token.function {
color: #f44336;
}
.token.regex,
.token.important,
.token.variable {
color: #ff9800;
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}

View file

@ -1,6 +1,6 @@
import type { Metadata } from "next";
import localFont from "next/font/local";
import { Roboto, Instrument_Sans } from "next/font/google";
import { Syne, Unbounded } from "next/font/google";
import "./globals.css";
import { Providers } from "./providers";
import MixpanelInitializer from "./MixpanelInitializer";
@ -16,16 +16,16 @@ const inter = localFont({
variable: "--font-inter",
});
const instrument_sans = Instrument_Sans({
const syne = Syne({
subsets: ["latin"],
weight: ["400"],
variable: "--font-instrument-sans",
weight: ["400", "500", "600", "700", "800"],
variable: "--font-syne",
});
const roboto = Roboto({
const unbounded = Unbounded({
subsets: ["latin"],
weight: ["400"],
variable: "--font-roboto",
weight: ["400", "500", "600", "700", "800"],
variable: "--font-unbounded",
});
@ -82,7 +82,7 @@ export default function RootLayout({
return (
<html lang="en">
<body
className={`${inter.variable} ${roboto.variable} ${instrument_sans.variable} antialiased`}
className={`${inter.variable} ${unbounded.variable} ${syne.variable} antialiased`}
>
<Providers>
<MixpanelInitializer>

View file

@ -19,6 +19,7 @@ import OnBoardingHeader from "./OnBoarding/OnBoardingHeader";
import ModeSelectStep from "./OnBoarding/ModeSelectStep";
import PresentonMode from "./OnBoarding/PresentonMode";
import GenerationWithImage from "./OnBoarding/GenerationWithImage";
import FinalStep from "./OnBoarding/FinalStep";
// Button state interface
interface ButtonState {
@ -30,6 +31,54 @@ interface ButtonState {
status?: string;
}
const FINAL_STEP_CONFETTI_PIECES = [
// left: denser at top
{ side: "left", offset: 1, top: 3, width: 28, height: 10, color: "#F59E0B", rotate: 12 },
{ side: "left", offset: 7, top: 5, width: 18, height: 7, color: "#7C3AED", rotate: -10 },
{ side: "left", offset: 12, top: 7, width: 20, height: 7, color: "#14B8A6", rotate: 22 },
{ side: "left", offset: 3, top: 10, width: 22, height: 8, color: "#22C55E", rotate: -18 },
{ side: "left", offset: 9, top: 12, width: 24, height: 8, color: "#E11D48", rotate: 18 },
{ side: "left", offset: 14, top: 15, width: 18, height: 7, color: "#F43F5E", rotate: 23 },
{ side: "left", offset: 5, top: 18, width: 20, height: 7, color: "#0EA5E9", rotate: -12 },
{ side: "left", offset: 11, top: 21, width: 26, height: 9, color: "#2563EB", rotate: 20 },
{ side: "left", offset: 2, top: 24, width: 19, height: 7, color: "#14B8A6", rotate: -16 },
{ side: "left", offset: 8, top: 28, width: 21, height: 8, color: "#FB7185", rotate: 27 },
{ side: "left", offset: 13, top: 32, width: 20, height: 7, color: "#06B6D4", rotate: 16 },
{ side: "left", offset: 3, top: 36, width: 24, height: 9, color: "#EAB308", rotate: -22 },
{ side: "left", offset: 10, top: 41, width: 18, height: 7, color: "#A855F7", rotate: -14 },
{ side: "left", offset: 2, top: 50, width: 30, height: 10, color: "#EC4899", rotate: -28 },
{ side: "left", offset: 13, top: 58, width: 19, height: 7, color: "#22C55E", rotate: 17 },
{ side: "left", offset: 5, top: 66, width: 24, height: 8, color: "#8B5CF6", rotate: 14 },
{ side: "left", offset: 11, top: 74, width: 18, height: 7, color: "#3B82F6", rotate: 12 },
{ side: "left", offset: 4, top: 82, width: 20, height: 7, color: "#14B8A6", rotate: 21 },
{ side: "left", offset: 7, top: 90, width: 24, height: 8, color: "#D946EF", rotate: -26 },
// right: denser at top
{ side: "right", offset: 1, top: 4, width: 30, height: 10, color: "#F97316", rotate: -14 },
{ side: "right", offset: 8, top: 6, width: 19, height: 7, color: "#0EA5E9", rotate: 12 },
{ side: "right", offset: 13, top: 9, width: 20, height: 7, color: "#22C55E", rotate: -20 },
{ side: "right", offset: 4, top: 12, width: 24, height: 8, color: "#EC4899", rotate: 20 },
{ side: "right", offset: 10, top: 15, width: 22, height: 8, color: "#06B6D4", rotate: -18 },
{ side: "right", offset: 15, top: 18, width: 20, height: 7, color: "#22C55E", rotate: -25 },
{ side: "right", offset: 5, top: 21, width: 18, height: 7, color: "#8B5CF6", rotate: 19 },
{ side: "right", offset: 12, top: 24, width: 21, height: 8, color: "#F43F5E", rotate: 14 },
{ side: "right", offset: 2, top: 28, width: 26, height: 9, color: "#84CC16", rotate: 15 },
{ side: "right", offset: 9, top: 33, width: 21, height: 8, color: "#F97316", rotate: -11 },
{ side: "right", offset: 14, top: 38, width: 20, height: 7, color: "#A855F7", rotate: -19 },
{ side: "right", offset: 4, top: 44, width: 19, height: 7, color: "#F43F5E", rotate: 20 },
{ side: "right", offset: 2, top: 52, width: 28, height: 10, color: "#FACC15", rotate: 25 },
{ side: "right", offset: 12, top: 60, width: 18, height: 7, color: "#14B8A6", rotate: -15 },
{ side: "right", offset: 6, top: 68, width: 24, height: 8, color: "#22C55E", rotate: -17 },
{ side: "right", offset: 1, top: 76, width: 20, height: 7, color: "#A855F7", rotate: 14 },
{ side: "right", offset: 13, top: 84, width: 20, height: 7, color: "#3B82F6", rotate: -24 },
{ side: "right", offset: 5, top: 92, width: 26, height: 9, color: "#EAB308", rotate: 18 },
] as const;
const getTaperedSideOffset = (offset: number, top: number) => {
const taperMultiplier = Math.max(0.72, 1.85 - top * 0.012);
return Math.min(29, Number((offset * taperMultiplier).toFixed(2)));
};
export default function Home() {
const router = useRouter();
const pathname = usePathname();
@ -37,7 +86,7 @@ export default function Home() {
const [selectedMode, setSelectedMode] = useState<string>("presenton")
const config = useSelector((state: RootState) => state.userConfig);
const [llmConfig, setLlmConfig] = useState<LLMConfig>(config.llm_config);
console.log('config', config);
const [downloadingModel, setDownloadingModel] = useState<{
name: string;
size: number | null;
@ -270,13 +319,34 @@ export default function Home() {
// </div>
// </div>
// </div>
<div className="flex h-screen ">
<div className="flex h-screen">
<OnBoardingSlidebar />
<main className="w-full pl-20 pr-8 max-w-[1440px] mx-auto relative">
<main className="w-full pl-20 pr-8 max-w-[1440px] mx-auto relative z-10">
{step === 3 && (
<div className="pointer-events-none fixed inset-0 z-0 overflow-hidden" aria-hidden>
{FINAL_STEP_CONFETTI_PIECES.map((piece, index) => (
<span
key={`${piece.side}-${index}`}
className="absolute rounded-[3px]"
style={{
top: `${piece.top}%`,
...(piece.side === "left"
? { left: `${getTaperedSideOffset(piece.offset, piece.top)}%` }
: { right: `${getTaperedSideOffset(piece.offset, piece.top)}%` }),
width: `${piece.width}px`,
height: `${piece.height}px`,
backgroundColor: piece.color,
transform: `rotate(${piece.rotate}deg)`,
}}
/>
))}
</div>
)}
<OnBoardingHeader currentStep={step} />
{step === 1 && <ModeSelectStep setStep={setStep} setSelectedMode={setSelectedMode} />}
{step === 2 && selectedMode === "presenton" && <PresentonMode currentStep={step} setStep={setStep} />}
{step === 2 && selectedMode === "image" && <GenerationWithImage />}
{step === 3 && <FinalStep />}
</main>
</div>
);

View file

@ -0,0 +1,31 @@
import { ArrowRight } from 'lucide-react'
import { usePathname, useRouter } from 'next/navigation'
import React from 'react'
import { trackEvent, MixpanelEvent } from "@/utils/mixpanel";
const FinalStep = () => {
const router = useRouter()
const pathname = usePathname()
const handleGoToDashboard = () => {
trackEvent(MixpanelEvent.Navigation, { from: pathname, to: "/dashboard" });
router.push('/dashboard')
}
const handleGoToUpload = () => {
trackEvent(MixpanelEvent.Navigation, { from: pathname, to: "/upload" });
router.push('/upload')
}
return (
<div className='fixed top-0 left-0 w-full h-full flex flex-col items-center justify-center'>
<div className='flex flex-col items-center justify-center'>
<img src="/final_onboarding.png" alt="presenton" className='w-[118px] h-[98px] object-contain' />
<h1 className='text-black text-[30px] font-normal font-unbounded py-2.5'>Welcome on board!</h1>
<p className='text-[#000000CC] text-xl font-normal font-syne'>Your AI workspace is ready. Lets create your first presentation.</p>
<button onClick={handleGoToUpload} className='bg-[#7C51F8] px-[23px] mt-14 py-[15px] rounded-[70px] text-white text-lg font-syne font-semibold'>My First Presentation</button>
</div>
<button onClick={handleGoToDashboard} className='absolute bottom-20 text-[#7A5AF8] flex items-center gap-2 right-10 text-xs font-normal font-syne'>Go to your dashboard <ArrowRight className='w-4 h-4 text-[#7A5AF8]' /></button>
</div>
)
}
export default FinalStep

View file

@ -6,14 +6,14 @@ const ModeSelectStep = ({ setStep, setSelectedMode }: { setStep: (step: number)
<div className='max-w-[650px]'>
<div className='mb-[70px]'>
<h2 className='mb-4 text-black text-[26px] font-normal '>Lets set up your AI workspace</h2>
<p className='text-[##000000CC] text-xl font-normal'>First, choose the intelligence behind your presentation generation.</p>
<h2 className='mb-4 text-black text-[26px] font-normal font-unbounded '>Lets set up your AI workspace</h2>
<p className='text-[#000000CC] text-xl font-normal font-syne'>First, choose the intelligence behind your presentation generation.</p>
</div>
<div className='space-y-5'>
<div onClick={() => {
setSelectedMode("presenton")
setStep(2)
}} className='border border-[#EDEEEF] rounded-[11px] p-3 flex items-center justify-between gap-6 cursor-pointer'>
}} className='border font-syne border-[#EDEEEF] rounded-[11px] p-3 flex items-center justify-between gap-6 cursor-pointer'>
<div className='flex items-center gap-6'>
<div className='rounded-[4px] bg-[#F4F3FF] p-[12px] w-[58px] h-[58px] flex items-center justify-center'>
<img src='/logo-with-bg.png' alt='presenton' className='w-full h-full object-contain' />
@ -21,18 +21,23 @@ const ModeSelectStep = ({ setStep, setSelectedMode }: { setStep: (step: number)
<div className=''>
<div className='flex items-start gap-2 relative '>
<h3 className='text-black text-[18px] font-medium'>Presenton</h3>
<h3 className='text-black text-[18px] font-medium font-syne'>Presenton</h3>
<p className='bg-[#F4F3FF] px-3 py-1.5 rounded-[30px] text-[#7A5AF8] text-[9px] absolute left-[95px] top-[-10px]'>PPTX</p>
</div>
<p className='text-[#999999] text-[14px] font-normal'>Optimized for fast, structured slide generation.</p>
<p className='text-[#999999] text-[14px] font-normal font-syne'>Optimized for fast, structured slide generation.</p>
</div>
</div>
<ChevronRight className='w-6 h-6 text-[#B3B3B3]' />
</div>
<div onClick={() => {
setSelectedMode("image")
setStep(2)
}} className='border border-[#EDEEEF] rounded-[11px] p-3 flex items-center justify-between gap-6 cursor-pointer'>
<div
// onClick={() => {
// setSelectedMode("image")
// setStep(2)
// }}
className='border font-syne border-[#EDEEEF] cursor-not-allowed rounded-[11px] p-3 flex items-center justify-between gap-6 relative'>
<p className='text-black absolute top-1/2 -translate-y-1/2 right-14 flex items-center justify-center text-[14px] font-normal bg-[#F4F3FF] px-3 py-1.5 rounded-[30px]'>Coming soon</p>
<div className='flex items-center gap-6'>
<div className='rounded-[4px] bg-[#FFF6ED] p-[12px] w-[58px] h-[58px] flex items-center justify-center'>
<img src='/image_mode.png' alt='presenton' className='w-full h-full object-contain' />
@ -40,10 +45,10 @@ const ModeSelectStep = ({ setStep, setSelectedMode }: { setStep: (step: number)
<div className=''>
<div className='flex items-start gap-2 relative '>
<h3 className='text-black text-[18px] font-medium'>Generate with Image Model</h3>
<h3 className='text-black text-[18px] font-medium font-syne'>Generate with Image Model</h3>
</div>
<p className='text-[#999999] text-[14px] font-normal'>Generate presentations with visual layouts and elements.</p>
<p className='text-[#999999] text-[14px] font-normal font-syne'>Generate presentations with visual layouts and elements.</p>
</div>
</div>
<ChevronRight className='w-6 h-6 text-[#B3B3B3]' />

View file

@ -3,10 +3,10 @@ import React from 'react'
const OnBoardingHeader = ({ currentStep }: { currentStep: number }) => {
return (
<div className='flex items-center justify-end gap-1 mt-7 mb-[52px]'>
<div className='relative z-20 flex items-center font-syne justify-end gap-1 mt-7 mb-[52px]'>
<div className='flex items-center gap-1'>
<div className={`${currentStep >= 1 ? 'bg-[#010100] text-white' : 'border border-[#ECECEF] text-[#494A4D]'} px-2.5 h-7 w-7 text-xs font-medium rounded-full flex items-center justify-center`}>
<div className={`${currentStep === 1 ? 'bg-[#010100] text-white' : 'border border-[#ECECEF] text-[#494A4D]'} px-2.5 h-7 w-7 text-xs font-medium rounded-full flex items-center justify-center`}>
1
</div>
<p className='text-[#010000] text-xs '>Select Mode</p>
@ -15,7 +15,7 @@ const OnBoardingHeader = ({ currentStep }: { currentStep: number }) => {
<path d="M0 0.5H21.5" stroke="#ECECEF" />
</svg>
<div className='flex items-center gap-1'>
<div className={`${currentStep >= 2 ? 'bg-[#010100] text-white' : 'border border-[#ECECEF] text-[#494A4D]'} px-2.5 h-7 w-7 text-xs font-medium rounded-full flex items-center justify-center`}>
<div className={`${currentStep === 2 ? 'bg-[#010100] text-white' : 'border border-[#ECECEF] text-[#494A4D]'} px-2.5 h-7 w-7 text-xs font-medium rounded-full flex items-center justify-center`}>
2
</div>
<p className='text-[#010000] text-xs '>Choose Providers</p>
@ -24,7 +24,7 @@ const OnBoardingHeader = ({ currentStep }: { currentStep: number }) => {
<path d="M0 0.5H21.5" stroke="#ECECEF" />
</svg>
<div className='flex items-center gap-1'>
<div className={`${currentStep >= 3 ? 'bg-[#010100] text-white' : 'border border-[#ECECEF] text-[#494A4D]'} px-2.5 h-7 w-7 text-xs font-medium rounded-full flex items-center justify-center`}>
<div className={`${currentStep === 3 ? 'bg-[#010100] text-white' : 'border border-[#ECECEF] text-[#494A4D]'} px-2.5 h-7 w-7 text-xs font-medium rounded-full flex items-center justify-center`}>
3
</div>
<p className='text-[#010000] text-xs '>Finish Setup</p>

View file

@ -289,14 +289,17 @@ const PresentonMode = ({ currentStep, setStep }: { currentStep: number, setStep:
}
}
toast.info("Configuration saved successfully");
setSavingConfig(false);
// Track navigation from -> to
trackEvent(MixpanelEvent.Navigation, { from: pathname, to: "/upload" });
router.push("/upload");
trackEvent(MixpanelEvent.Navigation, { from: pathname, to: "/final onboarding step" });
setStep(3)
// router.push("/upload");
} catch (error) {
toast.info(error instanceof Error ? error.message : "Failed to save configuration");
}
finally {
setSavingConfig(false);
}
};
const downloadProgress = useMemo(() => {
@ -308,12 +311,12 @@ const PresentonMode = ({ currentStep, setStep }: { currentStep: number, setStep:
return (
<div className='w-full max-w-[640px]'>
<p className='px-2.5 py-0.5 w-fit text-[#7A5AF8] rounded-[50px] border border-[#EDEEEF] text-[10px] font-medium mb-5'>PRESENTON</p>
<div className='w-full max-w-[640px] font-syne'>
<p className='px-2.5 py-0.5 w-fit text-[#7A5AF8] rounded-[50px] border border-[#EDEEEF] text-[10px] font-medium mb-5 font-syne'>PRESENTON</p>
<div className='mb-[54px]'>
<h2 className='mb-4 text-black text-[26px] font-normal '>Choose your content providers</h2>
<p className='text-[##000000CC] text-xl font-normal'>Select the AI engines that will generate your slide text and visuals.</p>
<h2 className='mb-4 text-black text-[26px] font-normal font-unbounded '>Choose your content providers</h2>
<p className='text-[#000000CC] text-xl font-normal font-syne'>Select the AI engines that will generate your slide text and visuals.</p>
</div>
{/* Text Provider */}
<div className='p-3 border border-[#EDEEEF] rounded-[11px] '>
@ -798,7 +801,7 @@ const PresentonMode = ({ currentStep, setStep }: { currentStep: number, setStep:
</button>
<button
disabled={savingConfig || downloadProgress > 0}
disabled={savingConfig}
onClick={handleSaveConfig}
className='border border-[#EDEEEF] bg-[#7C51F8] rounded-[58px] px-5 py-2.5 text-white text-xs font-semibold'>
Continue to Finish

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

View file

@ -82,9 +82,9 @@ const config: Config = {
"accordion-up": "accordion-up 0.2s ease-out",
},
fontFamily: {
instrument_sans: ["var(--font-instrument-sans)"],
syne: ["var(--font-syne)"],
unbounded: ["var(--font-unbounded)"],
inter: ["var(--font-inter)"],
roboto: ["var(--font-roboto)"],
},
},
},