Skip to content
Open
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
2 changes: 2 additions & 0 deletions app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,8 @@
.container {
@apply px-4 xl:px-6 mx-auto max-w-screen-2xl;
}


}

@layer base {
Expand Down
18 changes: 10 additions & 8 deletions app/home/HeroContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ import { Button } from '@/components/ui/button';
import { Icons } from '@/components/icon';
import { NumberTicker } from '@/components/magicui/number-ticker';
import Link from 'next/link';
import { useI18n } from '@/components/i18n-provider';
import { siteConfig } from '@/config/site';
import { useEffect, useState } from 'react';
import axios from 'axios';

export function HeroContent() {
const { t } = useI18n();
const [star, setStar] = useState(0);
const fetchGithubData = () => {
axios
Expand All @@ -38,24 +40,24 @@ export function HeroContent() {
<AnimatedBadge />
</AnimateEnter>
<AnimateEnter delay={0.3} duration={2}>
<h1 className="mx-auto text-center max-w-5xl px-4 font-bold text-2xl md:text-6xl leading-tight tracking-tight">
<span className="block text-gradient mb-1">Instant UI Components</span>
<h1 className="mx-auto text-center max-w-5xl px-4 font-bold text-2xl md:text-6xl leading-tight tracking-tight min-h-[1.2em] md:min-h-[2.4em] flex flex-col justify-center">
<span className="block text-gradient mb-1">{t('HomePage.heroTitle1')}</span>
<span className="">
Just Copy, Paste &amp; Done
{t('HomePage.heroTitle2')}
</span>
</h1>
</AnimateEnter>
</div>
<AnimateEnter delay={0.5} duration={2} className="mb-6 sm:mb-8">
<p className="container mx-auto md:max-w-lg text-[12px] sm:text-base text-foreground">
250+ Production ready components, built with Next.js, shadcn/ui and Tailwind CSS.
<p className="container mx-auto md:max-w-lg text-[12px] sm:text-base text-foreground min-h-[3em] sm:min-h-[4em] flex items-center justify-center">
{t('HomePage.heroDescription')}
</p>
</AnimateEnter>
<AnimateEnter className="flex items-center justify-center gap-3" delay={0.7} duration={2}>
<div className="flex flex-row items-center justify-center gap-3 mt-2 md:mt-6 w-full sm:w-auto px-4">
<Link href="/docs/installation" className="w-auto">
<Button size="lg" className="rounded-xl w-auto px-4 sm:px-8">
Explore Components
{t('HomePage.explore')}
</Button>
</Link>

Expand All @@ -64,7 +66,7 @@ export function HeroContent() {
<Button variant="secondary" className="rounded-xl w-auto px-4 sm:px-8 h-11">
<div className="flex items-center">
<Icons.gitHub className="size-4" />
<span className="ml-2 hidden sm:inline-block">Star on GitHub</span>
<span className="ml-2 hidden sm:inline-block">{t('HomePage.starGithub')}</span>
</div>
<div className="ml-2 flex items-center gap-1 text-sm">
🌟
Expand All @@ -79,7 +81,7 @@ export function HeroContent() {
<section className="w-full max-w-4xl mx-auto px-4 ">
<AnimateEnter delay={0.9} duration={2} className="space-y-6">
<h2 className="text-base font-medium text-muted-foreground uppercase tracking-wider">
Built With
{t('HomePage.builtWith')}
</h2>
<div className="grid grid-cols-2 sm:grid-cols-4 gap-6 place-items-center">
<Image
Expand Down
5 changes: 4 additions & 1 deletion app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
"use client";
import type { CSSProperties } from 'react';
import Image from 'next/image';
import dynamic from 'next/dynamic';
import { useI18n } from '@/components/i18n-provider';
const HomeCardCollection = dynamic(() => import('@/components/homecard'), {
ssr: true,
loading: () => <div className="animate-pulse h-[800px] w-full bg-neutral-100 dark:bg-neutral-900 rounded-lg"></div>
Expand All @@ -9,6 +11,7 @@ import { HeroSection } from './home';


const Homepage = () => {
const { t } = useI18n();
return (
<>
<div className="container-wrapper">
Expand Down Expand Up @@ -50,7 +53,7 @@ const Homepage = () => {
<div className="flex flex-col justify-center items-center my-12">
<div className="px-8 rounded-2xl text-center">
<h2 className="font-bold text-2xl md:text-4xl mb-4 tracking-tight text-neutral-900 dark:text-white">
Ready-to-Use UI Blocks
{t('HomePage.readyToUse')}
</h2>
</div>
</div>
Expand Down
14 changes: 1 addition & 13 deletions app/pro/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -459,19 +459,7 @@ export default function ProPage() {
</div>
</section>

{/* ── STATS — solid borders, not dashed ─────────────────────────────── */}
<section className="container-wrapper border-y
border-neutral-200 dark:border-neutral-800">
<div className="max-w-7xl mx-auto">
<div className="grid grid-cols-1 sm:grid-cols-3
divide-y sm:divide-y-0 sm:divide-x
divide-neutral-200 dark:divide-neutral-800">
<StatCard value="989+" label="GitHub Stars" />
<StatCard value="4,000+" label="Developers every month" />
<StatCard value="20,000+" label="Monthly pageviews" />
</div>
</div>
</section>


{/* ── TEMPLATES ─────────────────────────────────────────────────────── */}
<section className="container-wrapper border-b border-neutral-200 dark:border-neutral-800">
Expand Down
28 changes: 15 additions & 13 deletions components/cta.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
"use client";
import React from "react";
import Link from "next/link";
import { useI18n } from "./i18n-provider";
import { Button } from "./ui/button";

const Cta = () => {
const { t } = useI18n();
return (
<div className="flex flex-col items-center justify-center min-h-[40vh] py-5 mt-4 lg:mt-8 px-4">

Expand All @@ -13,25 +16,24 @@ const Cta = () => {
Spectrum Pro — Early Access
</div> */}

<h1 className="text-center text-3xl md:text-4xl lg:text-5xl
font-semibold mb-4 md:leading-tight text-foreground max-w-2xl">
Stop building from scratch.
<br />
<span className="bg-gradient-to-r from-[#6366F1] to-purple-400
bg-clip-text text-transparent">
Ship fast with Spectrum Pro.
<h1 className="text-center font-bold text-2xl md:text-5xl mb-4 leading-[1.1] tracking-tight text-foreground max-w-2xl min-h-[2.2em] md:min-h-[2em] flex flex-col justify-center gap-0">
<span className="block">{t('CTA.title1')}</span>
<span className="block">
{t('CTA.title2')}
<span className="text-gradient">
{t('CTA.title2Pro')}
</span>
</span>
</h1>

<p className="text-[#666] text-base mb-8 text-center max-w-md">
Premium Next.js templates built on Spectrum UI.
Dark. Animated. Production-ready. From $49.
<p className="text-[#666] text-base mb-8 text-center max-w-md min-h-[3em]">
{t('CTA.description')}
</p>

<div className="flex items-center gap-3">
<Link href="https://pro.spectrumhq.in">
<Button className="rounded-md px-6 text-sm font-medium">
Get access
<Link href="https://ui.spectrumhq.in/pro">
<Button className="rounded-3xl px-6 text-sm font-medium">
{t('CTA.button')}
</Button>
</Link>
</div>
Expand Down
43 changes: 31 additions & 12 deletions components/footer.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
"use client";
import Link from "next/link";

import { useI18n } from "./i18n-provider";
import { Icons } from "./icon";

export default function Footer() {
const { t, locale, setLocale } = useI18n();
return (
<footer className=" py-12 px-4 md:px-6 z-50">
<div className="container mx-auto">
Expand All @@ -18,7 +20,7 @@ export default function Footer() {
</Link>

<h1 className="dark:text-gray-300 mt-4">
Build by{" "}
{t('Footer.builtBy')}{" "}
<span className="dark:text-[#039ee4] gap-2">
<Link className="underline" href="https://x.com/arihantCodes">@Arihantjain</Link>
<span>{" & "}</span>
Expand All @@ -28,42 +30,42 @@ export default function Footer() {
</span>
</h1>

<p className="text-sm dark:text-gray-400 mt-5">
© {new Date().getFullYear()} Spectrum UI. All rights reserved.
<p className="text-sm dark:text-gray-400 mt-5 min-h-[1.5em]">
© {new Date().getFullYear()} Spectrum UI. {t('Footer.rights')}
</p>
</div>
<div className="grid grid-cols-2 md:grid-cols-4 gap-8">
<div>
<h3 className="font-semibold mb-4">Pages</h3>
<h3 className="font-semibold mb-4">{t('Footer.pages')}</h3>
<ul className="space-y-2">
<li>
<Link
href="/docs"
className="text-gray-600 hover:text-black dark:text-gray-400 dark:hover:text-white"
>
Docs
{t('Footer.docs')}
</Link>
</li>
<li>
<Link
href="/blocks"
className="text-gray-600 hover:text-black dark:text-gray-400 dark:hover:text-white"
>
Blocks
{t('Footer.blocks')}
</Link>
</li>
<li>
<Link
href="/colors"
className="text-gray-600 hover:text-black dark:text-gray-400 dark:hover:text-white"
>
Colors
{t('Footer.colors')}
</Link>
</li>
</ul>
</div>
<div>
<h3 className="font-semibold mb-4">Socials</h3>
<h3 className="font-semibold mb-4">{t('Footer.socials')}</h3>
<ul className="space-y-2">
<li>
<Link
Expand Down Expand Up @@ -92,34 +94,51 @@ export default function Footer() {
</ul>
</div>
<div>
<h3 className="font-semibold mb-4">Legal</h3>
<h3 className="font-semibold mb-4">{t('Footer.legal')}</h3>
<ul className="space-y-2">
<li>
<Link
href="/privacy-policy"
className="text-gray-600 hover:text-black dark:text-gray-400 dark:hover:text-white"
>
Privacy Policy
{t('Footer.privacy')}
</Link>
</li>
<li>
<Link
href="/tos"
className="text-gray-600 hover:text-black dark:text-gray-400 dark:hover:text-white"
>
Terms of Service
{t('Footer.tos')}
</Link>
</li>
<li>
<Link
href="/faqs"
className="text-gray-600 hover:text-black dark:text-gray-400 dark:hover:text-white"
>
FAQs
{t('Footer.faqs')}
</Link>
</li>
</ul>
</div>
<div>
<h3 className="font-semibold mb-4">{t('Footer.language')}</h3>
<select
value={locale}
onChange={(e) => setLocale(e.target.value as any)}
className="bg-background border rounded px-2 py-1 text-sm focus:outline-none focus:ring-1 focus:ring-primary"
>
<option value="en">English 🇺🇸</option>
<option value="es">Español 🇪🇸</option>
<option value="pt">Português 🇧🇷</option>
<option value="zh">简体中文 🇨🇳</option>
<option value="fr">Français 🇫🇷</option>
<option value="de">Deutsch 德</option>
<option value="hi">हिन्दी 🇮🇳</option>
<option value="tr">Türkçe 🇹🇷</option>
</select>
</div>
</div>
</div>
<div className=" w-full flex mt-4 items-center justify-center ">
Expand Down
64 changes: 64 additions & 0 deletions components/i18n-provider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
'use client';

import React, { createContext, useContext, useState, useEffect } from 'react';
import en from '../messages/en.json';
import es from '../messages/es.json';
import pt from '../messages/pt.json';
import zh from '../messages/zh.json';
import fr from '../messages/fr.json';
import de from '../messages/de.json';
import hi from '../messages/hi.json';
import tr from '../messages/tr.json';

const translations = { en, es, pt, zh, fr, de, hi, tr };

type Language = 'en' | 'es' | 'pt' | 'zh' | 'fr' | 'de' | 'hi' | 'tr';
type Translations = typeof en;

interface I18nContextProps {
locale: Language;
setLocale: (locale: Language) => void;
t: (key: string) => any;
}

const I18nContext = createContext<I18nContextProps | undefined>(undefined);

export const I18nProvider = ({ children }: { children: React.ReactNode }) => {
const [locale, setLocaleState] = useState<Language>('en');

useEffect(() => {
const savedLocale = localStorage.getItem('locale') as Language;
if (savedLocale && translations[savedLocale]) {
setLocaleState(savedLocale);
}
}, []);

const setLocale = (newLocale: Language) => {
setLocaleState(newLocale);
localStorage.setItem('locale', newLocale);
};

const t = (path: string) => {
const keys = path.split('.');
let current: any = translations[locale];
for (const key of keys) {
if (current[key] === undefined) return path;
current = current[key];
}
return current;
};

return (
<I18nContext.Provider value={{ locale, setLocale, t }}>
{children}
</I18nContext.Provider>
);
};

export const useI18n = () => {
const context = useContext(I18nContext);
if (!context) {
throw new Error('useI18n must be used within an I18nProvider');
}
return context;
};
Loading