<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: AIVCJ studio</title>
    <description>The latest articles on DEV Community by AIVCJ studio (@aivcj).</description>
    <link>https://dev.to/aivcj</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4237279%2Fda806276-bd36-46c8-9d80-290f4579088d.png</url>
      <title>DEV Community: AIVCJ studio</title>
      <link>https://dev.to/aivcj</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/aivcj"/>
    <language>en</language>
    <item>
      <title>Personal or Commercial licence: which one for a client site?</title>
      <dc:creator>AIVCJ studio</dc:creator>
      <pubDate>Tue, 10 Nov 2026 14:40:53 +0000</pubDate>
      <link>https://dev.to/aivcj/personal-or-commercial-licence-which-one-for-a-client-site-6kl</link>
      <guid>https://dev.to/aivcj/personal-or-commercial-licence-which-one-for-a-client-site-6kl</guid>
      <description>&lt;p&gt;For &lt;strong&gt;one website for one client&lt;/strong&gt;, the Personal licence is enough. You can build it, charge the client for your work and hand it over. If you will use the same theme for a &lt;strong&gt;second client&lt;/strong&gt;, or inside a &lt;strong&gt;product people pay to use&lt;/strong&gt; (a SaaS, an app, a paid tool), you need the Commercial licence.&lt;/p&gt;

&lt;p&gt;That covers most cases. The rest of this page answers the ones in between, using the exact terms on our &lt;a href="https://portfolio.aivcj.com/legal/license" rel="noopener noreferrer"&gt;License Agreement&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The difference in one table
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Personal&lt;/th&gt;
&lt;th&gt;Commercial&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;End products&lt;/td&gt;
&lt;td&gt;One, for you or for one client&lt;/td&gt;
&lt;td&gt;As many as you like, for you and any number of clients&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A product you sell to many users (SaaS, app, paid product)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Charge your client for your work&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Resell or share the files&lt;/td&gt;
&lt;td&gt;Never&lt;/td&gt;
&lt;td&gt;Never&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;An &lt;strong&gt;end product&lt;/strong&gt; is the finished thing: one website or one app. Staging and production copies of that same site count as one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Questions agencies ask us
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;I'm building one site for a client. They pay me, not you. Is Personal fine?&lt;/strong&gt;&lt;br&gt;
Yes. Personal covers one site for one client, and you can charge them for your work. Buy one Personal licence per client site.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I'll probably use this theme for three clients this year.&lt;/strong&gt;&lt;br&gt;
Then Commercial is the better buy. It covers every client project, now and later, and you can record each project in your account to get a licence certificate for that client.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My client wants the source code.&lt;/strong&gt;&lt;br&gt;
They get the finished site you built for them. They may not take the theme's files out of it and reuse them on another site. That rule is the same for both licences.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We're a team of five. Does everyone need a licence?&lt;/strong&gt;&lt;br&gt;
No. The licence belongs to the buyer: you, or your company if you bought for it. Everyone working for that company may use it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I'm building a SaaS on top of a dashboard kit like &lt;a href="https://portfolio.aivcj.com/kits/aurora" rel="noopener noreferrer"&gt;AURORA&lt;/a&gt;.&lt;/strong&gt;&lt;br&gt;
That needs Commercial. Your customers pay to use the product, which is exactly the case Personal excludes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I make my own template from it and sell that?&lt;/strong&gt;&lt;br&gt;
No, with either licence. You can't sell or share the files, change them and resell them, or make a product whose main value is the item itself (another theme, template or UI kit).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do updates cost extra?&lt;/strong&gt;&lt;br&gt;
No. Both licences get every update we publish, from your account.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How does my client know the licence is real?&lt;/strong&gt;&lt;br&gt;
Every purchase has a licence key (PULSE-XXXX-XXXX-XXXX). Your client can check it on our site; the check shows the item and the licence type, never your personal details.&lt;/p&gt;

&lt;h2&gt;
  
  
  Before you buy
&lt;/h2&gt;

&lt;p&gt;Sales are final once the files are delivered, so try first. Every theme has a live demo, for example &lt;a href="https://portfolio.aivcj.com/themes/lunetta" rel="noopener noreferrer"&gt;Lunetta&lt;/a&gt;, and our &lt;a href="https://portfolio.aivcj.com/pricing" rel="noopener noreferrer"&gt;pricing page&lt;/a&gt; shows both prices side by side. If the files don't work as described, we fix them or replace them.&lt;/p&gt;

&lt;p&gt;Not sure which one fits your project? &lt;a href="https://portfolio.aivcj.com/support" rel="noopener noreferrer"&gt;Write to us&lt;/a&gt; with a sentence about it. We'll tell you honestly, including when the cheaper one is enough.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;First published on &lt;a href="https://portfolio.aivcj.com/blog/personal-or-commercial-licence-for-a-client-site" rel="noopener noreferrer"&gt;Pulse by AIVCJ&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>licence</category>
      <category>agencies</category>
      <category>pricing</category>
    </item>
    <item>
      <title>How to re-brand a Next.js SaaS kit from one file</title>
      <dc:creator>AIVCJ studio</dc:creator>
      <pubDate>Tue, 10 Nov 2026 14:40:52 +0000</pubDate>
      <link>https://dev.to/aivcj/how-to-re-brand-a-nextjs-saas-kit-from-one-file-27jj</link>
      <guid>https://dev.to/aivcj/how-to-re-brand-a-nextjs-saas-kit-from-one-file-27jj</guid>
      <description>&lt;p&gt;Open &lt;code&gt;src/config/theme.ts&lt;/code&gt;, change &lt;code&gt;accent&lt;/code&gt; to your brand colour, and save. In AURORA that one value re-colours the marketing site and the dashboard: buttons, links, focus rings, soft fills, glows, the first chart line and the hero light. This guide shows what to change, why it works, and the two things to check before you ship.&lt;/p&gt;

&lt;p&gt;AURORA is our AI SaaS kit on Pulse. We built it so that a buyer's first job, making it look like their product, takes minutes instead of a day of hunting through components.&lt;/p&gt;

&lt;h2&gt;
  
  
  Before you start
&lt;/h2&gt;

&lt;p&gt;You need the kit running locally (&lt;code&gt;npm ci&lt;/code&gt;, then &lt;code&gt;npm run dev&lt;/code&gt;; the Documentation folder in the download walks through it). Have your brand colour as a hex value, and a second colour from the same family for the gradient.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: change the theme file
&lt;/h2&gt;

&lt;p&gt;This is the whole file a buyer edits (shortened here; the real file explains every value in a comment at the top).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/config/theme.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;theme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;accent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"#7458F7"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="c1"&gt;// your brand colour&lt;/span&gt;
  &lt;span class="n"&gt;accentTo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"#56CCF2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;// the gradient fades from accent into this&lt;/span&gt;
  &lt;span class="n"&gt;onAccent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"#FFFFFF"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;// text on a solid accent button&lt;/span&gt;
  &lt;span class="n"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;            &lt;span class="c1"&gt;// corner roundness in px (0 = sharp, 20 = soft)&lt;/span&gt;
  &lt;span class="n"&gt;fonts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"geist"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"inter"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;defaultMode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"dark"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;// or "light"; visitors can still switch&lt;/span&gt;
  &lt;span class="n"&gt;showThemeDial&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;// the "Try your colour" swatches in the hero&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save, and the dev server reloads with the new colour everywhere. There is no second place to change it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: check the text on your buttons
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;onAccent&lt;/code&gt; is the colour of text on a solid accent button. It has to stay readable. The rule we follow is the WCAG one: at least 4.5 to 1 contrast for normal text.&lt;/p&gt;

&lt;p&gt;Here are AURORA's own presets, with the ratio each one gets:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Accent&lt;/th&gt;
&lt;th&gt;Text on it&lt;/th&gt;
&lt;th&gt;Contrast&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Violet &lt;code&gt;#7458F7&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;white&lt;/td&gt;
&lt;td&gt;4.69 : 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cobalt &lt;code&gt;#2F6BEF&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;white&lt;/td&gt;
&lt;td&gt;4.70 : 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rose &lt;code&gt;#D4365F&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;white&lt;/td&gt;
&lt;td&gt;4.67 : 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Aurora green &lt;code&gt;#1FC8A0&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;near-black &lt;code&gt;#04140F&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;8.84 : 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amber &lt;code&gt;#F5A524&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;near-black &lt;code&gt;#1A1204&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;9.09 : 1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Light, bright accents like green and amber fail with white text (about 2 : 1), so they get near-black text instead. If your brand colour is light, do the same. Any contrast checker will give you the number; aim for 4.5 or more.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: remove the colour dial before you launch
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;showThemeDial: true&lt;/code&gt; shows six swatches in the hero so people trying the demo can see the kit in other colours. Your customers don't need it. Set it to &lt;code&gt;false&lt;/code&gt; and the swatches and their small script are gone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why one value is enough
&lt;/h2&gt;

&lt;p&gt;The trick is that nothing else in the kit names a colour. Everything is derived from the accent in one stylesheet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* src/app/globals.css (shortened) */&lt;/span&gt;
&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nt"&gt;root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;--accent-soft&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;color-mix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;oklab&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;--accent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="kt"&gt;%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;transparent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nv"&gt;--accent-line&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;color-mix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;oklab&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;--accent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="kt"&gt;%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;transparent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nv"&gt;--accent-glow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;color-mix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;oklab&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;--accent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="kt"&gt;%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;transparent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nv"&gt;--aurora&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;linear-gradient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100deg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;--accent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nf"&gt;color-mix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;oklab&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;--accent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;--accent-to&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="mi"&gt;55&lt;/span&gt;&lt;span class="kt"&gt;%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;--accent-to&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;color-mix()&lt;/code&gt; blends two colours in the browser. Mixing in &lt;code&gt;oklab&lt;/code&gt; keeps the blends looking even, so a 14% tint of rose looks as light as a 14% tint of blue. Components only ever use &lt;code&gt;--accent-soft&lt;/code&gt;, &lt;code&gt;--accent-line&lt;/code&gt; and the rest, never a hex value.&lt;/p&gt;

&lt;p&gt;The root layout writes your &lt;code&gt;theme.ts&lt;/code&gt; values into &lt;code&gt;:root&lt;/code&gt; once, on the server, so the first paint is already in your colour. No flash of violet before your brand loads.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the live colour change animates
&lt;/h2&gt;

&lt;p&gt;The demo's dial changes the colour smoothly over 0.6 seconds. Normally a CSS variable can't animate: the browser doesn't know it is a colour, so it jumps. Registering it tells the browser its type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@property&lt;/span&gt; &lt;span class="nv"&gt;--accent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;syntax&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;color&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;inherits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;initial-value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#7458f7&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nt"&gt;root&lt;/span&gt;&lt;span class="nc"&gt;.dial-anim&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;--accent&lt;/span&gt; &lt;span class="mf"&gt;0.6s&lt;/span&gt; &lt;span class="n"&gt;ease&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;--accent-to&lt;/span&gt; &lt;span class="mf"&gt;0.6s&lt;/span&gt; &lt;span class="n"&gt;ease&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The dial adds the &lt;code&gt;dial-anim&lt;/code&gt; class, sets the new values, and removes the class 0.7 seconds later. If the visitor has reduced motion turned on, it skips the class and the colour changes at once.&lt;/p&gt;

&lt;p&gt;You can use the same three lines in any project that keeps its brand colour in a CSS variable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dark mode, for free
&lt;/h2&gt;

&lt;p&gt;Colours that differ between light and dark are written once with &lt;code&gt;light-dark()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nv"&gt;--accent-text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;light-dark&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nf"&gt;color-mix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;in&lt;/span&gt; &lt;span class="nt"&gt;oklab&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;--accent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="mi"&gt;78&lt;/span&gt;&lt;span class="kt"&gt;%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;#000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nf"&gt;color-mix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;in&lt;/span&gt; &lt;span class="nt"&gt;oklab&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;--accent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="mi"&gt;58&lt;/span&gt;&lt;span class="kt"&gt;%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;#fff&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Accent-coloured text is darkened on light backgrounds and lightened on dark ones, from the same accent. You never pick a separate "dark mode brand colour", and the two modes can't drift apart.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it doesn't do
&lt;/h2&gt;

&lt;p&gt;It doesn't change your product name, logo, fonts beyond Geist and Inter, or images. Those are separate steps in the README: the name lives in &lt;code&gt;src/content/site.ts&lt;/code&gt;, the logo files in &lt;code&gt;public/assets/&lt;/code&gt;, and other fonts load through &lt;code&gt;next/font/local&lt;/code&gt;. It also can't make a colour accessible for you; Step 2 is still your job.&lt;/p&gt;

&lt;p&gt;If you want to see it before you buy, the dial is live on the &lt;a href="https://portfolio.aivcj.com/kits/aurora" rel="noopener noreferrer"&gt;AURORA demo&lt;/a&gt;. Pick a swatch and watch the dashboard follow.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;First published on &lt;a href="https://portfolio.aivcj.com/blog/rebrand-a-nextjs-saas-kit-from-one-file" rel="noopener noreferrer"&gt;Pulse by AIVCJ&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>css</category>
      <category>theming</category>
      <category>saas</category>
    </item>
    <item>
      <title>What broke when we set up our own templates as a buyer</title>
      <dc:creator>AIVCJ studio</dc:creator>
      <pubDate>Tue, 10 Nov 2026 14:40:51 +0000</pubDate>
      <link>https://dev.to/aivcj/what-broke-when-we-set-up-our-own-templates-as-a-buyer-4obl</link>
      <guid>https://dev.to/aivcj/what-broke-when-we-set-up-our-own-templates-as-a-buyer-4obl</guid>
      <description>&lt;p&gt;Most of it worked. All 15 zips installed and built on a clean machine. But we found three packages that were far bigger than they should have been, missing setup docs, a licence file that named the wrong licences, and five bugs a buyer would have hit on their first day. This is the list, with the fixes.&lt;/p&gt;

&lt;p&gt;We sell themes and kits on Pulse. Until last week we tested each one as its builder: in the project folder, with our own tools around it. A buyer gets something else. They get a zip, a fresh machine and a README. So we stopped testing the project and started testing the zip.&lt;/p&gt;

&lt;h2&gt;
  
  
  How we tested
&lt;/h2&gt;

&lt;p&gt;We wrote one script that does what a buyer does, in order, for every product in the store:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Unzip the download into an empty folder.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;npm ci&lt;/code&gt;, then a type check.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;npm run dev&lt;/code&gt; and open the home page.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;npm run build&lt;/code&gt;, then &lt;code&gt;npm start&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Open up to 10 pages at 360, 768, 1280 and 1536 pixels wide, with reduced motion on.&lt;/li&gt;
&lt;li&gt;Compare every page with the hosted demo the buyer saw before paying.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It ran on two Node versions, 24.22 and 22.23, because buyers don't all update at the same time. That came to &lt;strong&gt;348 page checks&lt;/strong&gt;, each compared with the live demo. We also loaded the pages again with the clock set a year ahead, which found a bug we would never have seen.&lt;/p&gt;

&lt;h2&gt;
  
  
  What was wrong with the zips
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Build folders in the download.&lt;/strong&gt; Three zips carried our local build output (&lt;code&gt;.next-check&lt;/code&gt; and &lt;code&gt;.next-dev&lt;/code&gt;). Sorted was 77.2 MB. After the fix it is 1.2 MB. Maun went from 22.9 MB to 7.0 MB. A buyer would have downloaded tens of megabytes of files that were useless to them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Our own files.&lt;/strong&gt; Some zips had our internal check scripts, a &lt;code&gt;.forge.json&lt;/code&gt;, TypeScript build caches and a package name of &lt;code&gt;dp-theme&lt;/code&gt;. None of that is harmful, but it is noise, and it tells a buyer nobody looked inside the zip.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The wrong README.&lt;/strong&gt; Three products shipped our starter kit's README instead of their own. A buyer opening the folder would read about a different project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No licence file, or the wrong one.&lt;/strong&gt; Most zips had no licence file. AURORA had one, but it said "Single" and "Unlimited". Pulse sells Personal and Commercial licences, so the zip and the store disagreed.&lt;/p&gt;

&lt;p&gt;The fix was one packaging script that every product now goes through. It copies only what the buyer needs, writes the product's own README, adds the Pulse &lt;code&gt;LICENSE.txt&lt;/code&gt;, adds &lt;code&gt;.env.example&lt;/code&gt; when the code reads environment variables, checks the lockfile matches &lt;code&gt;package.json&lt;/code&gt;, and sets &lt;code&gt;engines&lt;/code&gt; to Node 20.9 or newer, so npm warns a buyer whose Node is too old.&lt;/p&gt;

&lt;h2&gt;
  
  
  Five bugs a buyer would have hit
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. A page that scrolled sideways on phones.&lt;/strong&gt; Lunetta's cabins page moved 250 px to the right at 360 px wide. The table had its own scroll area, but the hidden labels for screen readers sat outside it and stretched the page. The hosted demo had the same bug. We moved the labels inside the scroll area.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. and 3. Two smaller sideways scrolls.&lt;/strong&gt; Peel's home page by 8 px (a flag on the map) and Sorted's pricing row by 3 px. Small, but on a phone it feels like the page is loose.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. A chart label that broke hydration.&lt;/strong&gt; AURORA's dashboard showed React error #418. The server drew a chart axis label as &lt;code&gt;$0&lt;/code&gt; and the browser drew &lt;code&gt;$0.0&lt;/code&gt;. Both are fine on their own. When they differ, React throws away the server HTML and redraws the page. The label went through a compact number format, and Node and the browser did not agree on it. We now pick one explicit format per axis, with fixed decimals, so both sides print the same text.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Dates frozen at build time.&lt;/strong&gt; This is the one the year-ahead load found, in three products. The swatch-test theme shows delivery dates, and the two résumé themes show "this year". Those were written into the HTML when the site was built. Open the site on a later day and the browser computes a different date than the HTML says, and React throws #418 again. A buyer who built the site once and visited it next month would have seen it. We now compute those dates in the browser after the page loads.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fportfolio.aivcj.com%2Fapi%2Fmedia%2Fblog%2Fwhat-broke-when-we-set-up-our-templates-as-a-buyer%2Fdocs-step-4c64761d8d.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fportfolio.aivcj.com%2Fapi%2Fmedia%2Fblog%2Fwhat-broke-when-we-set-up-our-templates-as-a-buyer%2Fdocs-step-4c64761d8d.webp" title="Each step says what to run and what you should see after" alt="A setup step in the new Documentation guide, with the command to run and what you should see"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup docs in every zip
&lt;/h2&gt;

&lt;p&gt;Each download now has a &lt;code&gt;Documentation/index.html&lt;/code&gt; that works offline. It walks through six steps, from unzip to a live site, with diagrams of what happens at each step, real screenshots of the product, a tour of the pages, fixes for the problems people usually hit, credits for fonts and images, and how to reach us. It reads well on a phone too.&lt;/p&gt;

&lt;p&gt;We checked the guide the same way as the products: 15 guides at 4 widths in light and dark mode, 120 checks, nothing failing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The numbers after the fixes
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Before&lt;/th&gt;
&lt;th&gt;After&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Largest zip&lt;/td&gt;
&lt;td&gt;77.2 MB (Sorted)&lt;/td&gt;
&lt;td&gt;25.2 MB (Lunetta)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Zips with our build folders inside&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Zips with the product's own README, Pulse licence and setup guide&lt;/td&gt;
&lt;td&gt;none had all three&lt;/td&gt;
&lt;td&gt;15 of 15&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Products with a bug a buyer would hit&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;0 (plus 1 one-off we could not repeat)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Second round: 14 of 15 products had zero snags. Playable Résumé showed one #418 once on its home page at 360 px, and not again in 31 tries. We are still watching it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we'd tell anyone selling templates
&lt;/h2&gt;

&lt;p&gt;Test the download, not the project. Your project folder has things the buyer's never will, and it hides exactly the bugs they hit first. Set the clock forward a year at least once. And read your own README as if you had never seen the code.&lt;/p&gt;

&lt;p&gt;Every Pulse download, including &lt;a href="https://portfolio.aivcj.com/kits/aurora" rel="noopener noreferrer"&gt;AURORA&lt;/a&gt; and &lt;a href="https://portfolio.aivcj.com/themes/lunetta" rel="noopener noreferrer"&gt;Lunetta&lt;/a&gt;, is now built by this packaging script. If something still doesn't work as described, &lt;a href="https://portfolio.aivcj.com/support" rel="noopener noreferrer"&gt;write to us&lt;/a&gt; and we fix it.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;First published on &lt;a href="https://portfolio.aivcj.com/blog/what-broke-when-we-set-up-our-templates-as-a-buyer" rel="noopener noreferrer"&gt;Pulse by AIVCJ&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>codequality</category>
      <category>templates</category>
      <category>hydration</category>
    </item>
  </channel>
</rss>
