Radiant:精美的新行銷網站範本

Dan Hollick

我們剛剛完成了一個名為 Radiant 的精美全新 SaaS 行銷網站範本,現在已可作為 Tailwind UI 的一部分使用。

Learn about the Radiant template

它使用 Next.js、Framer Motion 和 Tailwind CSS 建構,並由 Sanity 提供部落格功能。

我們已經有一段時間沒有建構像這樣的 SaaS 行銷範本了,在那段時間裡,我們學到了很多關於如何使這樣的範本實用且易於使用的知識。我們已嘗試將所有這些知識融入 Radiant 中。

一如既往,請查看即時預覽以獲得完整的體驗 — 其中有許多很酷的細節,您必須在瀏覽器中查看才能真正體會。


精緻的互動性

在像這樣的網站上過度使用動畫非常容易。我們都看過一些網站,您甚至無法滾動幾個像素,就會有一堆不同的元素動畫到位。更糟的是,您必須等待內容出現才能閱讀時,會感覺速度很慢。

Radiant 載入了令人愉悅的動畫,但它們都分層在現有內容上,並由使用者互動觸發,因此網站仍然感覺快速。在大多數情況下,我們選擇了循環動畫,使元素在您與它們互動時感覺「栩栩如生」。

我們幾乎將所有動畫都使用了 Framer Motion。它是宣告式的,使我們可以輕鬆地為複雜的動畫建立自己的 API,其他人可以輕鬆自訂這些動畫而無需太多精力。

但是,它也有一些缺點需要克服。例如,當您有多個元素獨立動畫時,將懸停狀態傳遞給每個子元素很麻煩。我們最終利用 Framer 的變體傳播來使其工作 — 懸停事件會觸發父元素中的變體變更,該變更會傳播到子元素,因為它們共享相同的變體鍵。

bento-card.tsx
export function BentoCard() {  return (    <motion.div      initial="idle"      whileHover="active"      variants={{ idle: {}, active: {} }}      data-dark={dark ? "true" : undefined}    >      /* ... */    </motion.div>  );}

父元素中的變體沒有任何差異,因此它實際上不會變更,但子元素仍然會在懸停時收到變更變體的訊號,即使它們是深層巢狀的。

map.tsx
function Marker({  src,  top,  offset,  delay,}: {  src: string  top: number  offset: number  delay: number}) {  return (    <motion.div      variants={{        idle: { scale: 0, opacity: 0, rotateX: 0, rotate: 0, y: 0 },        active: { y: [-20, 0, 4, 0], scale: [0.75, 1], opacity: [0, 1] },      }}      transition={{ duration: 0.25, delay, ease: 'easeOut' }}      style={{ '--offset': `${offset}px`, top } as React.CSSProperties}      className="absolute left-[calc(50%+var(--offset))] size-[38px] drop-shadow-[0_3px_1px_rgba(0,0,0,.15)]"    >      /* ... */    </motion.div>  )}/* ... */

標誌時間軸動畫有點不同,因為我們希望標誌在您停止懸停時暫停在目前位置,而不是返回其原始位置。這與 Framer 指定開始和結束狀態的方法不太相容,因此實際上在 CSS 中建構此動畫更容易。

它利用了您可以設定負的 animation-delay 值來偏移元素的起始位置的事實。這樣,所有標誌都共享相同的動畫關鍵影格,但它們可以從不同的位置開始,並具有不同的持續時間。

logo-timeline.tsx
function Logo({  label,  src,  className,}: {  label: string  src: string  className: string}) {  return (    <div      className={clsx(        className,        'absolute top-2 grid grid-cols-[1rem,1fr] items-center gap-2 whitespace-nowrap px-3 py-1',        'rounded-full bg-gradient-to-t from-gray-800 from-50% to-gray-700 ring-1 ring-inset ring-white/10',        '[--move-x-from:-100%] [--move-x-to:calc(100%+100cqw)] [animation-iteration-count:infinite] [animation-name:move-x] [animation-play-state:paused] [animation-timing-function:linear] group-hover:[animation-play-state:running]',      )}    >      <img alt="" src={src} className="size-4" />      <span className="text-sm/6 font-medium text-white">{label}</span>    </div>  )}export function LogoTimeline() {  return (    /* ... */    <Row>      <Logo        label="Loom"        src="./logo-timeline/loom.svg"        className="[animation-delay:-26s] [animation-duration:30s]"      />      <Logo        label="Gmail"        src="./logo-timeline/gmail.svg"        className="[animation-delay:-8s] [animation-duration:30s]"      />    </Row>    /* ... */

這種方法表示我們不需要在 JavaScript 中追蹤播放狀態,我們可以使用 group-hover:[animation-play-state:running] 類別在懸停父元素時啟動動畫。

您可能已經注意到,我們在這個元件中使用了許多個別 animation 屬性的任意屬性,因為這些實用程式今天在 Tailwind 中不存在。這就是建構這些範本的優點 — 它有助於我們發現 Tailwind CSS 中的盲點。誰知道呢,也許我們會在 v4.0 中看到加入這些實用程式!


刻意可重複使用

設計像這樣的 SaaS 範本最棘手的部分,是想出人們可以應用於自己產品的互動元素,而無需太多精力。沒有什麼比購買範本後發現它太特定於範例內容,而您實際上無法將其用於自己的專案更糟糕的了。

我們想出了一些大多數 SaaS 產品可能具有的核心圖形元素。一個帶有圖釘的地圖、一個標誌群、一個鍵盤 — 這些東西可以應用於許多不同的功能。因為我們希望它們易於為您自己的產品重新調整用途,所以我們以程式碼建構了許多這些元素,並為它們設計了不錯的 API。

例如,標誌群具有一個簡單的 API,可讓您傳入自己的標誌,調整它們的位置和懸停動畫以符合要求。

<Logo src="./logo-cluster/dribbble.svg" left={285} top={20} hover={{ x: 4, y: -5, rotate: 6, delay: 0.3 }} />

鍵盤快速鍵部分是另一個很好的例子。新增您自己的快速鍵就像將按鍵名稱陣列傳遞給 Keyboard 元件一樣簡單,而且由於每個按鍵都是一個元件,您可以輕鬆新增自訂按鍵或變更配置。

<Keyboard highlighted={["F", "M", "L"]} />

事實證明,以程式碼建構鍵盤實際上需要大量的工作,但至少現在您永遠不必自己發現這一點。

當然,我們也為您留下了一些位置,供您放置自己產品的螢幕截圖。以下是為我們的朋友 SavvyCal 客製化的此部分的樣子,使用了相同的互動式元件。

Radiant as SavvyCal

由 CMS 支援

通常,當我們向範本新增部落格時,我們只使用 MDX,但這次我們認為可以改為嘗試使用無頭 CMS。在對我們的觀眾進行調查並聽到許多好評之後,我們決定嘗試使用 Sanity

CMS 讓您可以從它們的 UI 處理所有事務,而無需建立檔案、進行提交和手動管理圖像等,因此即使是非開發人員也可以輕鬆貢獻。

Sanity Studio

像 Sanity 這樣的無頭 CMS 的一個很酷的地方是,您可以以結構化格式取回您的內容,因此與 MDX 類似,您可以將元素對應到您自己的自訂元件來處理您的所有排版樣式。

<PortableText  value={post.body}  components={{    block: {      normal: ({ children }) => <p className="my-10 text-base/8 first:mt-0 last:mb-0">{children}</p>,      h2: ({ children }) => (        <h2 className="mt-12 mb-10 text-2xl/8 font-medium tracking-tight text-gray-950 first:mt-0 last:mb-0">          {children}        </h2>      ),      h3: ({ children }) => (        <h3 className="mt-12 mb-10 text-xl/8 font-medium tracking-tight text-gray-950 first:mt-0 last:mb-0">          {children}        </h3>      ),      blockquote: ({ children }) => (        <blockquote className="my-10 border-l-2 border-l-gray-300 pl-6 text-base/8 text-gray-950 first:mt-0 last:mb-0">          {children}        </blockquote>      ),    },    types: {      image: ({ value }) => (        <img className="w-full rounded-2xl" src={image(value).width(2000).url()} alt={value.alt || ""} />      ),    },    /* ... */  }}/>

使用 CMS 也表示您的所有資產(例如圖像)都為您託管,並且您可以隨時控制圖像的大小、品質和格式。

<div className="text-sm/5 max-sm:text-gray-700 sm:font-medium">  {dayjs(post.publishedAt).format('dddd, MMMM D, YYYY')}</div>{post.author && (  <div className="mt-2.5 flex items-center gap-3">    {post.author.image && (      <img        className="aspect-square size-6 rounded-full object-cover"        src={image(post.author.image).width(64).height(64).url()}        alt=""      />    )}    <div className="text-sm/5 text-gray-700">      {post.author.name}    </div>  </div>)}

就像您在 Markdown 中使用前文一樣,您也可以使用自訂欄位來豐富內容。例如,我們在部落格文章架構中新增了一個 featured 布林值欄位,因此您可以在部落格的特殊部分中突出顯示某些文章。

Radiant Blog

Sanity 特別是一種付費產品,但它們有一個相當慷慨的免費層級,足以讓您玩玩。而且,如果您想嘗試不同的無頭 CMS,我認為我們在這裡整合的 Sanity 仍然可以作為一個很好的資訊範例,說明您如何使用其他工具來處理這些事情。


這就是 Radiant!看看它在幕後是如何運作的、試用一下,並讓我們知道您的想法。

與我們所有的範本一樣,它包含在一次性購買的 Tailwind UI 全面存取授權中,這是支持我們在 Tailwind CSS 上工作的最佳方式,並使我們能夠在未來幾年繼續為您建構很棒的東西。

將我們所有的更新直接發送到您的收件匣。
註冊我們的電子報。